[ILM] Policy phases redesign (#88671)

* Phases redesign

* Title and name field layout

* Active highlight wip

* Copy comments

* Updated data allocation dropdown

* Min age error message

* Fixed tests

* Fixed edit policy integration tests

* Fixed more tests

* Cleaned up test files

* Use hotProperty instead of a string

* Clean up in phase component

* Fixed i18n files

* Updated optional fields

* Updated aria attributes after running axe tests

* Added review suggestions

* Reversed data allocation field changes

* Fixed type error

* Reversed on/off label and prepend input label

* Deleted property consts from phases components

* Removed not needed i18n consts and added i18n where missing

* Fixed merge conflicts with master

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yulia Čech 2021-01-27 17:59:44 +01:00 committed by GitHub
parent 0a7a9e4933
commit d931ed61e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1030 additions and 1248 deletions

View file

@ -21,6 +21,9 @@ const PERCENT_SIGN_NAME = 'test%';
const PERCENT_SIGN_WITH_OTHER_CHARS_NAME = 'test%#';
const PERCENT_SIGN_25_SEQUENCE = 'test%25';
const createPolicyTitle = 'Create Policy';
const editPolicyTitle = 'Edit Policy';
window.scrollTo = jest.fn();
jest.mock('@elastic/eui', () => {
@ -52,7 +55,7 @@ describe('<App />', () => {
await actions.clickCreatePolicyButton();
component.update();
expect(testBed.find('policyTitle').text()).toBe(`Create an index lifecycle policy`);
expect(testBed.find('policyTitle').text()).toBe(createPolicyTitle);
expect(testBed.find('policyNameField').props().value).toBe('');
});
@ -68,7 +71,7 @@ describe('<App />', () => {
await actions.clickCreatePolicyButton();
component.update();
expect(testBed.find('policyTitle').text()).toBe(`Create an index lifecycle policy`);
expect(testBed.find('policyTitle').text()).toBe(createPolicyTitle);
expect(testBed.find('policyNameField').props().value).toBe('');
});
});
@ -89,9 +92,7 @@ describe('<App />', () => {
await actions.clickPolicyNameLink();
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${SPECIAL_CHARS_NAME}`
);
expect(testBed.find('policyTitle').text()).toBe(`${editPolicyTitle} ${SPECIAL_CHARS_NAME}`);
});
test('loading edit policy page url works', async () => {
@ -102,9 +103,7 @@ describe('<App />', () => {
const { component } = testBed;
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${SPECIAL_CHARS_NAME}`
);
expect(testBed.find('policyTitle').text()).toBe(`${editPolicyTitle} ${SPECIAL_CHARS_NAME}`);
});
// using double encoding to counteract react-router's v5 internal decodeURI call
@ -117,9 +116,7 @@ describe('<App />', () => {
const { component } = testBed;
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${SPECIAL_CHARS_NAME}`
);
expect(testBed.find('policyTitle').text()).toBe(`${editPolicyTitle} ${SPECIAL_CHARS_NAME}`);
});
});
@ -136,9 +133,7 @@ describe('<App />', () => {
const { component } = testBed;
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_NAME}`
);
expect(testBed.find('policyTitle').text()).toBe(`${editPolicyTitle} ${PERCENT_SIGN_NAME}`);
});
test('loading edit policy page url with double encoding works', async () => {
@ -149,9 +144,7 @@ describe('<App />', () => {
const { component } = testBed;
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_NAME}`
);
expect(testBed.find('policyTitle').text()).toBe(`${editPolicyTitle} ${PERCENT_SIGN_NAME}`);
});
});
@ -174,7 +167,7 @@ describe('<App />', () => {
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
`${editPolicyTitle} ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
);
});
@ -188,7 +181,7 @@ describe('<App />', () => {
// known issue https://github.com/elastic/kibana/issues/82440
expect(testBed.find('policyTitle').text()).not.toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
`${editPolicyTitle} ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
);
});
@ -203,7 +196,7 @@ describe('<App />', () => {
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
`${editPolicyTitle} ${PERCENT_SIGN_WITH_OTHER_CHARS_NAME}`
);
});
});
@ -225,7 +218,7 @@ describe('<App />', () => {
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_25_SEQUENCE}`
`${editPolicyTitle} ${PERCENT_SIGN_25_SEQUENCE}`
);
});
@ -239,7 +232,7 @@ describe('<App />', () => {
// known issue https://github.com/elastic/kibana/issues/82440
expect(testBed.find('policyTitle').text()).not.toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_25_SEQUENCE}`
`${editPolicyTitle} ${PERCENT_SIGN_25_SEQUENCE}`
);
});
@ -254,7 +247,7 @@ describe('<App />', () => {
component.update();
expect(testBed.find('policyTitle').text()).toBe(
`Edit index lifecycle policy ${PERCENT_SIGN_25_SEQUENCE}`
`${editPolicyTitle} ${PERCENT_SIGN_25_SEQUENCE}`
);
});
});

View file

@ -89,6 +89,13 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
component.update();
};
const createFormCheckboxAction = (dataTestSubject: string) => async (checked: boolean) => {
await act(async () => {
form.selectCheckBox(dataTestSubject, checked);
});
component.update();
};
function createFormSetValueAction<V extends string = string>(dataTestSubject: string) {
return async (value: V) => {
await act(async () => {
@ -146,17 +153,21 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
forceMergeFieldExists: () => exists(toggleSelector),
toggleForceMerge: createFormToggleAction(toggleSelector),
setForcemergeSegmentsCount: createFormSetValueAction(`${phase}-selectedForceMergeSegments`),
setBestCompression: createFormToggleAction(`${phase}-bestCompression`),
setBestCompression: createFormCheckboxAction(`${phase}-bestCompression`),
};
};
const setIndexPriority = (phase: Phases) =>
createFormSetValueAction(`${phase}-phaseIndexPriority`);
const createIndexPriorityActions = (phase: Phases) => {
const toggleSelector = `${phase}-indexPrioritySwitch`;
return {
indexPriorityExists: () => exists(toggleSelector),
toggleIndexPriority: createFormToggleAction(toggleSelector),
setIndexPriority: createFormSetValueAction(`${phase}-indexPriority`),
};
};
const enable = (phase: Phases) => createFormToggleAction(`enablePhaseSwitch-${phase}`);
const warmPhaseOnRollover = createFormToggleAction(`warm-warmPhaseOnRollover`);
const setMinAgeValue = (phase: Phases) => createFormSetValueAction(`${phase}-selectedMinimumAge`);
const setMinAgeUnits = (phase: Phases) =>
@ -190,13 +201,15 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
await createFormSetValueAction(`${phase}-selectedReplicaCount`)(value);
};
const setShrink = (phase: Phases) => async (value: string) => {
await createFormToggleAction(`${phase}-shrinkSwitch`)(true);
await createFormSetValueAction(`${phase}-selectedPrimaryShardCount`)(value);
const createShrinkActions = (phase: Phases) => {
const toggleSelector = `${phase}-shrinkSwitch`;
return {
shrinkExists: () => exists(toggleSelector),
toggleShrink: createFormToggleAction(toggleSelector),
setShrink: createFormSetValueAction(`${phase}-primaryShardCount`),
};
};
const shrinkExists = (phase: Phases) => () => exists(`${phase}-shrinkSwitch`);
const setFreeze = createFormToggleAction('freezeSwitch');
const freezeExists = () => exists('freezeSwitch');
@ -250,25 +263,22 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
toggleRollover,
toggleDefaultRollover,
...createForceMergeActions('hot'),
setIndexPriority: setIndexPriority('hot'),
setShrink: setShrink('hot'),
shrinkExists: shrinkExists('hot'),
...createIndexPriorityActions('hot'),
...createShrinkActions('hot'),
setReadonly: setReadonly('hot'),
...createSearchableSnapshotActions('hot'),
},
warm: {
enable: enable('warm'),
warmPhaseOnRollover,
setMinAgeValue: setMinAgeValue('warm'),
setMinAgeUnits: setMinAgeUnits('warm'),
setDataAllocation: setDataAllocation('warm'),
setSelectedNodeAttribute: setSelectedNodeAttribute('warm'),
setReplicas: setReplicas('warm'),
setShrink: setShrink('warm'),
shrinkExists: shrinkExists('warm'),
...createShrinkActions('warm'),
...createForceMergeActions('warm'),
setReadonly: setReadonly('warm'),
setIndexPriority: setIndexPriority('warm'),
...createIndexPriorityActions('warm'),
},
cold: {
enable: enable('cold'),
@ -279,7 +289,7 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
setReplicas: setReplicas('cold'),
setFreeze,
freezeExists,
setIndexPriority: setIndexPriority('cold'),
...createIndexPriorityActions('cold'),
...createSearchableSnapshotActions('cold'),
},
delete: {

View file

@ -76,9 +76,6 @@ describe('<EditPolicy />', () => {
max_size: '50gb',
unknown_setting: 123, // Made up setting that should stay preserved
},
set_priority: {
priority: 100,
},
},
min_age: '0ms',
},
@ -126,8 +123,10 @@ describe('<EditPolicy />', () => {
await actions.hot.toggleForceMerge(true);
await actions.hot.setForcemergeSegmentsCount('123');
await actions.hot.setBestCompression(true);
await actions.hot.toggleShrink(true);
await actions.hot.setShrink('2');
await actions.hot.setReadonly(true);
await actions.hot.toggleIndexPriority(true);
await actions.hot.setIndexPriority('123');
await actions.savePolicy();
@ -186,13 +185,7 @@ describe('<EditPolicy />', () => {
const hotActions = policy.phases.hot.actions;
const rolloverAction = hotActions.rollover;
expect(rolloverAction).toBe(undefined);
expect(hotActions).toMatchInlineSnapshot(`
Object {
"set_priority": Object {
"priority": 100,
},
}
`);
expect(hotActions).toMatchInlineSnapshot(`Object {}`);
});
test('enabling searchable snapshot should hide force merge, freeze and shrink in subsequent phases', async () => {
@ -260,6 +253,7 @@ describe('<EditPolicy />', () => {
"priority": 50,
},
},
"min_age": "0ms",
}
`);
});
@ -270,6 +264,7 @@ describe('<EditPolicy />', () => {
await actions.warm.setDataAllocation('node_attrs');
await actions.warm.setSelectedNodeAttribute('test:123');
await actions.warm.setReplicas('123');
await actions.warm.toggleShrink(true);
await actions.warm.setShrink('123');
await actions.warm.toggleForceMerge(true);
await actions.warm.setForcemergeSegmentsCount('123');
@ -290,9 +285,6 @@ describe('<EditPolicy />', () => {
"max_age": "30d",
"max_size": "50gb",
},
"set_priority": Object {
"priority": 100,
},
},
"min_age": "0ms",
},
@ -316,24 +308,12 @@ describe('<EditPolicy />', () => {
"number_of_shards": 123,
},
},
"min_age": "0ms",
},
},
}
`);
});
test('setting warm phase on rollover to "false"', async () => {
const { actions } = testBed;
await actions.warm.enable(true);
await actions.warm.warmPhaseOnRollover(false);
await actions.warm.setMinAgeValue('123');
await actions.warm.setMinAgeUnits('d');
await actions.savePolicy();
const latestRequest = server.requests[server.requests.length - 1];
const warmPhaseMinAge = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm
.min_age;
expect(warmPhaseMinAge).toBe('123d');
});
});
describe('policy with include and exclude', () => {
@ -458,9 +438,6 @@ describe('<EditPolicy />', () => {
"max_age": "30d",
"max_size": "50gb",
},
"set_priority": Object {
"priority": 100,
},
},
"min_age": "0ms",
},
@ -662,9 +639,6 @@ describe('<EditPolicy />', () => {
"allocate": Object {
"number_of_replicas": 123,
},
"set_priority": Object {
"priority": 50,
},
}
`);
});

View file

@ -157,7 +157,7 @@ const setPhaseIndexPriority = async (
phase: string,
priority: string | number
) => {
const priorityInput = findTestSubject(rendered, `${phase}-phaseIndexPriority`);
const priorityInput = findTestSubject(rendered, `${phase}-indexPriority`);
await act(async () => {
priorityInput.simulate('change', { target: { value: priority } });
});
@ -324,9 +324,6 @@ describe('edit policy', () => {
max_age: '30d',
max_size: '50gb',
},
set_priority: {
priority: 100,
},
},
min_age: '0ms',
},
@ -451,6 +448,7 @@ describe('edit policy', () => {
const rendered = mountWithIntl(component);
await noRollover(rendered);
await setPolicyName(rendered, 'mypolicy');
await setPhaseIndexPriority(rendered, 'hot', '-1');
waitForFormLibValidation(rendered);
expectedErrorMessages(rendered, [i18nTexts.editPolicy.errors.nonNegativeNumberRequired]);
@ -512,7 +510,7 @@ describe('edit policy', () => {
});
rendered.update();
await setPhaseAfter(rendered, 'warm', '1');
const shrinkInput = findTestSubject(rendered, 'warm-selectedPrimaryShardCount');
const shrinkInput = findTestSubject(rendered, 'warm-primaryShardCount');
await act(async () => {
shrinkInput.simulate('change', { target: { value: '0' } });
});
@ -529,7 +527,7 @@ describe('edit policy', () => {
findTestSubject(rendered, 'warm-shrinkSwitch').simulate('click');
});
rendered.update();
const shrinkInput = findTestSubject(rendered, 'warm-selectedPrimaryShardCount');
const shrinkInput = findTestSubject(rendered, 'warm-primaryShardCount');
await act(async () => {
shrinkInput.simulate('change', { target: { value: '-1' } });
});
@ -845,7 +843,7 @@ describe('edit policy', () => {
await activatePhase(rendered, 'warm');
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
// Assert that only the custom and off options exist
// Assert that default, custom and 'none' options exist
findTestSubject(rendered, 'dataTierSelect').simulate('click');
expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeTruthy();
expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy();
@ -885,7 +883,7 @@ describe('edit policy', () => {
await activatePhase(rendered, 'warm');
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
// Assert that only the custom and off options exist
// Assert that default, custom and 'none' options exist
findTestSubject(rendered, 'dataTierSelect').simulate('click');
expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeFalsy();
expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy();

View file

@ -4,16 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import {
SerializedPhase,
DeletePhase,
SerializedPolicy,
RolloverAction,
} from '../../../common/types';
import { SerializedPolicy, RolloverAction } from '../../../common/types';
export const defaultSetPriority: string = '100';
export const defaultPhaseIndexPriority: string = '50';
export const defaultIndexPriority = {
hot: '100',
warm: '50',
cold: '0',
};
export const defaultRolloverAction: RolloverAction = {
max_age: '30d',
@ -30,15 +27,3 @@ export const defaultPolicy: SerializedPolicy = {
},
},
};
export const defaultNewDeletePhase: DeletePhase = {
phaseEnabled: false,
selectedMinimumAge: '0',
selectedMinimumAgeUnits: 'd',
waitForSnapshotPolicy: '',
};
export const serializedPhaseInitialization: SerializedPhase = {
min_age: '0ms',
actions: {},
};

View file

@ -0,0 +1,16 @@
.ilmActivePhaseHighlight {
border-left: $euiBorderWidthThin solid $euiColorLightShade;
height: 100%;
&.hotPhase.active {
border-left-color: $euiColorVis9_behindText;
}
&.warmPhase.active {
border-left-color: $euiColorVis5_behindText;
}
&.coldPhase.active {
border-left-color: $euiColorVis1_behindText;
}
}

View file

@ -0,0 +1,17 @@
/*
* 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, { FunctionComponent } from 'react';
import './active_highlight.scss';
interface Props {
phase: 'hot' | 'warm' | 'cold';
enabled: boolean;
}
export const ActiveHighlight: FunctionComponent<Props> = ({ phase, enabled }) => {
return <div className={`ilmActivePhaseHighlight ${phase}Phase ${enabled ? 'active' : ''} `} />;
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { MinAgeInputField } from './min_age_input_field';
export { ActiveHighlight } from './active_highlight';

View file

@ -11,6 +11,7 @@ export { OptionalLabel } from './optional_label';
export { PolicyJsonFlyout } from './policy_json_flyout';
export { DescribedFormRow, ToggleFieldWithDescribedFormRow } from './described_form_row';
export { FieldLoadingError } from './field_loading_error';
export { ActiveHighlight } from './active_highlight';
export { Timeline } from './timeline';
export * from './phases';

View file

@ -9,28 +9,21 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import { EuiDescribedFormGroup, EuiTextColor, EuiAccordion } from '@elastic/eui';
import { EuiTextColor } from '@elastic/eui';
import { Phases } from '../../../../../../../common/types';
import { useFormData } from '../../../../../../shared_imports';
import { useFormData, UseField, ToggleField, NumericField } from '../../../../../../shared_imports';
import { useEditPolicyContext } from '../../../edit_policy_context';
import { useConfigurationIssues } from '../../../form';
import {
LearnMoreLink,
ActiveBadge,
DescribedFormRow,
ToggleFieldWithDescribedFormRow,
} from '../../';
import { LearnMoreLink, ToggleFieldWithDescribedFormRow } from '../../';
import {
MinAgeInputField,
DataTierAllocationField,
SetPriorityInputField,
SearchableSnapshotField,
IndexPriorityField,
ReplicasField,
} from '../shared_fields';
import { Phase } from '../phase';
const i18nTexts = {
dataTierAllocation: {
@ -41,166 +34,64 @@ const i18nTexts = {
},
};
const coldProperty: keyof Phases = 'cold';
const formFieldPaths = {
enabled: '_meta.cold.enabled',
searchableSnapshot: 'phases.cold.actions.searchable_snapshot.snapshot_repository',
};
export const ColdPhase: FunctionComponent = () => {
const { policy } = useEditPolicyContext();
const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues();
const [formData] = useFormData({
watch: [formFieldPaths.enabled, formFieldPaths.searchableSnapshot],
watch: [formFieldPaths.searchableSnapshot],
});
const enabled = get(formData, formFieldPaths.enabled);
const showReplicasField = get(formData, formFieldPaths.searchableSnapshot) == null;
return (
<div id="coldPhaseContent" aria-live="polite" role="region">
<>
{/* Section title group; containing min age */}
<EuiDescribedFormGroup
<Phase phase={'cold'}>
<SearchableSnapshotField phase={'cold'} />
{showReplicasField && <ReplicasField phase={'cold'} />}
{/* Freeze section */}
{!isUsingSearchableSnapshotInHotPhase && (
<ToggleFieldWithDescribedFormRow
title={
<div>
<h2 className="eui-displayInlineBlock eui-alignMiddle">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseLabel"
defaultMessage="Cold phase"
/>
</h2>{' '}
{enabled && <ActiveBadge />}
</div>
}
titleSize="s"
description={
<>
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescriptionText"
defaultMessage="You are querying your index less frequently, so you can allocate shards
on significantly less performant hardware.
Because your queries are slower, you can reduce the number of replicas."
/>
</p>
<UseField
path={formFieldPaths.enabled}
component={ToggleField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': 'enablePhaseSwitch-cold',
'aria-controls': 'coldPhaseContent',
},
}}
<h3>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText"
defaultMessage="Freeze"
/>
</>
</h3>
}
description={
<EuiTextColor color="subdued">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText"
defaultMessage="Make the index read-only and minimize its memory footprint."
/>{' '}
<LearnMoreLink docPath="ilm-freeze.html" />
</EuiTextColor>
}
fullWidth
titleSize="xs"
switchProps={{
'data-test-subj': 'freezeSwitch',
path: '_meta.cold.freezeEnabled',
}}
>
{enabled && <MinAgeInputField phase="cold" />}
</EuiDescribedFormGroup>
{enabled && (
<>
<SearchableSnapshotField phase="cold" />
<div />
</ToggleFieldWithDescribedFormRow>
)}
<EuiAccordion
id="ilmWarmPhaseAdvancedSettings"
buttonContent={i18n.translate(
'xpack.indexLifecycleMgmt.warmPhase.advancedSettingsButton',
{
defaultMessage: 'Advanced settings',
}
)}
paddingSize="m"
>
{
/* Replicas section */
showReplicasField && (
<DescribedFormRow
title={
<h3>
{i18n.translate('xpack.indexLifecycleMgmt.coldPhase.replicasTitle', {
defaultMessage: 'Replicas',
})}
</h3>
}
description={i18n.translate(
'xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasDescription',
{
defaultMessage:
'Set the number of replicas. Remains the same as the previous phase by default.',
}
)}
switchProps={{
'data-test-subj': 'cold-setReplicasSwitch',
label: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.coldPhase.numberOfReplicas.switchLabel',
{ defaultMessage: 'Set replicas' }
),
initialValue:
policy.phases.cold?.actions?.allocate?.number_of_replicas != null,
}}
fullWidth
>
<UseField
path="phases.cold.actions.allocate.number_of_replicas"
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${coldProperty}-selectedReplicaCount`,
min: 0,
},
}}
/>
</DescribedFormRow>
)
}
{/* Data tier allocation section */}
<DataTierAllocationField
description={i18nTexts.dataTierAllocation.description}
phase={'cold'}
/>
{/* Freeze section */}
{!isUsingSearchableSnapshotInHotPhase && (
<ToggleFieldWithDescribedFormRow
title={
<h3>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText"
defaultMessage="Freeze"
/>
</h3>
}
description={
<EuiTextColor color="subdued">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText"
defaultMessage="Make the index read-only and minimize its memory footprint."
/>{' '}
<LearnMoreLink docPath="ilm-freeze.html" />
</EuiTextColor>
}
fullWidth
titleSize="xs"
switchProps={{
'data-test-subj': 'freezeSwitch',
path: '_meta.cold.freezeEnabled',
}}
>
<div />
</ToggleFieldWithDescribedFormRow>
)}
{/* Data tier allocation section */}
<DataTierAllocationField
description={i18nTexts.dataTierAllocation.description}
phase={coldProperty}
/>
<SetPriorityInputField phase={coldProperty} />
</EuiAccordion>
</>
)}
</>
</div>
<IndexPriorityField phase={'cold'} />
</Phase>
);
};

View file

@ -13,7 +13,7 @@ import { useFormData, UseField, ToggleField } from '../../../../../../shared_imp
import { ActiveBadge, LearnMoreLink, OptionalLabel } from '../../index';
import { MinAgeInputField, SnapshotPoliciesField } from '../shared_fields';
import { MinAgeField, SnapshotPoliciesField } from '../shared_fields';
const formFieldPaths = {
enabled: '_meta.delete.enabled',
@ -63,7 +63,7 @@ export const DeletePhase: FunctionComponent = () => {
}
fullWidth
>
{enabled && <MinAgeInputField phase="delete" />}
{enabled && <MinAgeField phase="delete" />}
</EuiDescribedFormGroup>
{enabled ? (
<EuiDescribedFormGroup

View file

@ -12,16 +12,12 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiDescribedFormGroup,
EuiCallOut,
EuiAccordion,
EuiTextColor,
EuiSwitch,
EuiIconTip,
} from '@elastic/eui';
import { Phases } from '../../../../../../../common/types';
import { useFormData, UseField, SelectField, NumericField } from '../../../../../../shared_imports';
import { i18nTexts } from '../../../i18n_texts';
@ -32,20 +28,19 @@ import { useEditPolicyContext } from '../../../edit_policy_context';
import { ROLLOVER_FORM_PATHS, isUsingDefaultRolloverPath } from '../../../constants';
import { LearnMoreLink, ActiveBadge, DescribedFormRow } from '../../';
import { LearnMoreLink, DescribedFormRow } from '../../';
import {
ForcemergeField,
SetPriorityInputField,
IndexPriorityField,
SearchableSnapshotField,
ReadonlyField,
ShrinkField,
} from '../shared_fields';
import { Phase } from '../phase';
import { maxSizeStoredUnits, maxAgeUnits } from './constants';
const hotProperty: keyof Phases = 'hot';
export const HotPhase: FunctionComponent = () => {
const { license } = useEditPolicyContext();
const [formData] = useFormData({
@ -56,245 +51,210 @@ export const HotPhase: FunctionComponent = () => {
const [showEmptyRolloverFieldsError, setShowEmptyRolloverFieldsError] = useState(false);
return (
<>
<EuiDescribedFormGroup
fullWidth
titleSize="s"
<Phase phase={'hot'}>
<DescribedFormRow
title={
<div>
<h2 className="eui-displayInlineBlock eui-alignMiddle">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseLabel"
defaultMessage="Hot phase"
/>
</h2>{' '}
<ActiveBadge />
</div>
<h3>
{i18n.translate('xpack.indexLifecycleMgmt.hotPhase.rolloverFieldTitle', {
defaultMessage: 'Rollover',
})}
</h3>
}
description={
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseDescriptionMessage"
defaultMessage="This phase is required. You are actively querying and
writing to your index. For faster updates, you can roll over the index when it gets too big or too old."
/>
</p>
}
>
<div />
</EuiDescribedFormGroup>
<EuiAccordion
id="ilmHotPhaseAdvancedSettings"
buttonContent={i18n.translate('xpack.indexLifecycleMgmt.hotPhase.advancedSettingsButton', {
defaultMessage: 'Advanced settings',
})}
paddingSize="m"
>
<DescribedFormRow
title={
<h3>
{i18n.translate('xpack.indexLifecycleMgmt.hotPhase.rolloverFieldTitle', {
defaultMessage: 'Rollover',
})}
</h3>
}
description={
<>
<EuiTextColor color="subdued">
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDescriptionMessage"
defaultMessage="Automate rollover of time series data for efficient storage and higher performance."
/>{' '}
<LearnMoreLink
text={
<>
<EuiTextColor color="subdued">
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDescriptionMessage"
defaultMessage="Automate rollover of time series data for efficient storage and higher performance."
/>{' '}
<LearnMoreLink
text={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.learnAboutRolloverLinkText"
defaultMessage="Learn more"
/>
}
docPath="ilm-rollover.html"
/>
</p>
</EuiTextColor>
<EuiSpacer />
<UseField<boolean> path={isUsingDefaultRolloverPath}>
{(field) => (
<>
<EuiSwitch
label={field.label}
checked={field.value}
onChange={(e) => field.setValue(e.target.checked)}
data-test-subj="useDefaultRolloverSwitch"
/>
&nbsp;
<EuiIconTip
type="questionInCircle"
content={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.learnAboutRolloverLinkText"
defaultMessage="Learn more"
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDefaultsTipContent"
defaultMessage="Rollover when an index is 30 days old or reaches 50 gigabytes."
/>
}
docPath="ilm-rollover.html"
/>
</p>
</EuiTextColor>
<EuiSpacer />
<UseField<boolean> path={isUsingDefaultRolloverPath}>
{(field) => (
<>
<EuiSwitch
label={field.label}
checked={field.value}
onChange={(e) => field.setValue(e.target.checked)}
data-test-subj="useDefaultRolloverSwitch"
/>
&nbsp;
<EuiIconTip
type="questionInCircle"
content={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDefaultsTipContent"
defaultMessage="Rollover when an index is 30 days old or reaches 50 gigabytes."
/>
}
/>
</>
)}
</UseField>
</>
}
fullWidth
>
{isUsingDefaultRollover === false ? (
<div aria-live="polite" role="region">
<UseField<boolean> path="_meta.hot.customRollover.enabled">
{(field) => (
<>
<EuiSwitch
label={field.label}
checked={field.value}
onChange={(e) => field.setValue(e.target.checked)}
data-test-subj="rolloverSwitch"
/>
&nbsp;
<EuiIconTip
type="questionInCircle"
content={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.enableRolloverTipContent"
defaultMessage="Roll over to a new index when the
current index meets one of the defined conditions."
/>
}
/>
</>
)}
</UseField>
{isUsingRollover && (
<>
<EuiSpacer size="m" />
{showEmptyRolloverFieldsError && (
<>
<EuiCallOut
title={i18nTexts.editPolicy.errors.rollOverConfigurationCallout.title}
data-test-subj="rolloverSettingsRequired"
color="danger"
>
<div>{i18nTexts.editPolicy.errors.rollOverConfigurationCallout.body}</div>
</EuiCallOut>
<EuiSpacer size="s" />
</>
)}
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField path={ROLLOVER_FORM_PATHS.maxSize}>
{(field) => {
const showErrorCallout = field.errors.some(
(e) => e.code === ROLLOVER_EMPTY_VALIDATION
);
if (showErrorCallout !== showEmptyRolloverFieldsError) {
setShowEmptyRolloverFieldsError(showErrorCallout);
}
return (
<NumericField
field={field}
euiFieldProps={{
'data-test-subj': `${hotProperty}-selectedMaxSizeStored`,
min: 1,
}}
/>
);
}}
</UseField>
</EuiFlexItem>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
key="_meta.hot.customRollover.maxStorageSizeUnit"
path="_meta.hot.customRollover.maxStorageSizeUnit"
component={SelectField}
componentProps={{
'data-test-subj': `${hotProperty}-selectedMaxSizeStoredUnits`,
hasEmptyLabelSpace: true,
euiFieldProps: {
options: maxSizeStoredUnits,
'aria-label': i18n.translate(
'xpack.indexLifecycleMgmt.hotPhase.maximumIndexSizeUnitsAriaLabel',
{
defaultMessage: 'Maximum index size units',
}
),
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
path={ROLLOVER_FORM_PATHS.maxDocs}
component={NumericField}
componentProps={{
euiFieldProps: {
'data-test-subj': `${hotProperty}-selectedMaxDocuments`,
min: 1,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
path={ROLLOVER_FORM_PATHS.maxAge}
component={NumericField}
componentProps={{
euiFieldProps: {
'data-test-subj': `${hotProperty}-selectedMaxAge`,
min: 1,
},
}}
/>
</EuiFlexItem>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
key="_meta.hot.customRollover.maxAgeUnit"
path="_meta.hot.customRollover.maxAgeUnit"
component={SelectField}
componentProps={{
'data-test-subj': `${hotProperty}-selectedMaxAgeUnits`,
hasEmptyLabelSpace: true,
euiFieldProps: {
'aria-label': i18n.translate(
'xpack.indexLifecycleMgmt.hotPhase.maximumAgeUnitsAriaLabel',
{
defaultMessage: 'Maximum age units',
}
),
options: maxAgeUnits,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</div>
) : (
<div />
)}
</DescribedFormRow>
{isUsingRollover && (
<>
{<ForcemergeField phase="hot" />}
<ShrinkField phase="hot" />
{license.canUseSearchableSnapshot() && <SearchableSnapshotField phase="hot" />}
<ReadonlyField phase={'hot'} />
</UseField>
</>
}
fullWidth
>
{isUsingDefaultRollover === false ? (
<div aria-live="polite" role="region">
<UseField<boolean> path="_meta.hot.customRollover.enabled">
{(field) => (
<>
<EuiSwitch
label={field.label}
checked={field.value}
onChange={(e) => field.setValue(e.target.checked)}
data-test-subj="rolloverSwitch"
/>
&nbsp;
<EuiIconTip
type="questionInCircle"
content={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hotPhase.enableRolloverTipContent"
defaultMessage="Roll over to a new index when the
current index meets one of the defined conditions."
/>
}
/>
</>
)}
</UseField>
{isUsingRollover && (
<>
<EuiSpacer size="m" />
{showEmptyRolloverFieldsError && (
<>
<EuiCallOut
title={i18nTexts.editPolicy.errors.rollOverConfigurationCallout.title}
data-test-subj="rolloverSettingsRequired"
color="danger"
>
<div>{i18nTexts.editPolicy.errors.rollOverConfigurationCallout.body}</div>
</EuiCallOut>
<EuiSpacer size="s" />
</>
)}
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField path={ROLLOVER_FORM_PATHS.maxSize}>
{(field) => {
const showErrorCallout = field.errors.some(
(e) => e.code === ROLLOVER_EMPTY_VALIDATION
);
if (showErrorCallout !== showEmptyRolloverFieldsError) {
setShowEmptyRolloverFieldsError(showErrorCallout);
}
return (
<NumericField
field={field}
euiFieldProps={{
'data-test-subj': `hot-selectedMaxSizeStored`,
min: 1,
}}
/>
);
}}
</UseField>
</EuiFlexItem>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
key="_meta.hot.customRollover.maxStorageSizeUnit"
path="_meta.hot.customRollover.maxStorageSizeUnit"
component={SelectField}
componentProps={{
'data-test-subj': `hot-selectedMaxSizeStoredUnits`,
hasEmptyLabelSpace: true,
euiFieldProps: {
options: maxSizeStoredUnits,
'aria-label': i18n.translate(
'xpack.indexLifecycleMgmt.hotPhase.maximumIndexSizeUnitsAriaLabel',
{
defaultMessage: 'Maximum index size units',
}
),
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
path={ROLLOVER_FORM_PATHS.maxDocs}
component={NumericField}
componentProps={{
euiFieldProps: {
'data-test-subj': `hot-selectedMaxDocuments`,
min: 1,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
path={ROLLOVER_FORM_PATHS.maxAge}
component={NumericField}
componentProps={{
euiFieldProps: {
'data-test-subj': `hot-selectedMaxAge`,
min: 1,
},
}}
/>
</EuiFlexItem>
<EuiFlexItem style={{ maxWidth: 188 }}>
<UseField
key="_meta.hot.customRollover.maxAgeUnit"
path="_meta.hot.customRollover.maxAgeUnit"
component={SelectField}
componentProps={{
'data-test-subj': `hot-selectedMaxAgeUnits`,
hasEmptyLabelSpace: true,
euiFieldProps: {
'aria-label': i18n.translate(
'xpack.indexLifecycleMgmt.hotPhase.maximumAgeUnitsAriaLabel',
{
defaultMessage: 'Maximum age units',
}
),
options: maxAgeUnits,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</div>
) : (
<div />
)}
<SetPriorityInputField phase={hotProperty} />
</EuiAccordion>
</>
</DescribedFormRow>
{isUsingRollover && (
<>
{<ForcemergeField phase={'hot'} />}
<ShrinkField phase={'hot'} />
{license.canUseSearchableSnapshot() && <SearchableSnapshotField phase={'hot'} />}
<ReadonlyField phase={'hot'} />
</>
)}
<IndexPriorityField phase={'hot'} />
</Phase>
);
};

View file

@ -11,3 +11,5 @@ export { WarmPhase } from './warm_phase';
export { ColdPhase } from './cold_phase';
export { DeletePhase } from './delete_phase';
export { Phase } from './phase';

View file

@ -0,0 +1,119 @@
/*
* 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, { FunctionComponent, useState } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiTitle,
EuiSpacer,
EuiText,
EuiButtonEmpty,
} from '@elastic/eui';
import { get } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
import { ToggleField, UseField, useFormData } from '../../../../../shared_imports';
import { i18nTexts } from '../../i18n_texts';
import { ActiveHighlight } from '../active_highlight';
import { MinAgeField } from './shared_fields';
interface Props {
phase: 'hot' | 'warm' | 'cold';
}
export const Phase: FunctionComponent<Props> = ({ children, phase }) => {
const enabledPath = `_meta.${phase}.enabled`;
const [formData] = useFormData({
watch: [enabledPath],
});
// hot phase is always enabled
const enabled = get(formData, enabledPath) || phase === 'hot';
const [isShowingSettings, setShowingSettings] = useState<boolean>(false);
return (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<ActiveHighlight phase={phase} enabled={enabled} />
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel hasShadow={enabled}>
<EuiFlexGroup wrap>
<EuiFlexItem>
<EuiFlexGroup alignItems="center" gutterSize={'s'}>
{phase !== 'hot' && (
<EuiFlexItem grow={false}>
<UseField
path={enabledPath}
component={ToggleField}
componentProps={{
euiFieldProps: {
'data-test-subj': `enablePhaseSwitch-${phase}`,
showLabel: false,
},
}}
/>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiTitle size={'s'}>
<h2>{i18nTexts.editPolicy.titles[phase]}</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{enabled && (
<EuiFlexItem>
<EuiFlexGroup
justifyContent="spaceBetween"
alignItems="center"
gutterSize={'xs'}
wrap
>
<EuiFlexItem grow={true}>
{phase !== 'hot' && <MinAgeField phase={phase} />}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`${phase}-settingsSwitch`}
onClick={() => {
setShowingSettings(!isShowingSettings);
}}
size="xs"
iconType="controlsVertical"
iconSide="left"
aria-controls={`${phase}-phaseContent`}
>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.phaseSettings.buttonLabel"
defaultMessage="Settings"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiSpacer />
<EuiText color="subdued" size={'s'} style={{ maxWidth: '50%' }}>
{i18nTexts.editPolicy.descriptions[phase]}
</EuiText>
{enabled && (
<div style={isShowingSettings ? {} : { display: 'none' }} id={`${phase}-phaseContent`}>
<EuiSpacer />
{children}
</div>
)}
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -6,9 +6,8 @@
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSpacer, EuiTextColor } from '@elastic/eui';
import { UseField, ToggleField, NumericField } from '../../../../../../shared_imports';
import { UseField, CheckBoxField, NumericField } from '../../../../../../shared_imports';
import { i18nTexts } from '../../../i18n_texts';
@ -38,50 +37,43 @@ export const ForcemergeField: React.FunctionComponent<Props> = ({ phase }) => {
</h3>
}
description={
<EuiTextColor color="subdued">
<>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.forceMerge.enableExplanationText"
defaultMessage="Reduce the number of segments in your shard by merging smaller files and clearing deleted ones."
/>{' '}
<LearnMoreLink docPath="ilm-forcemerge.html" />
</EuiTextColor>
</>
}
titleSize="xs"
fullWidth
switchProps={{
'aria-label': i18nTexts.editPolicy.forceMergeEnabledFieldLabel,
'data-test-subj': `${phase}-forceMergeSwitch`,
'aria-controls': 'forcemergeContent',
label: i18nTexts.editPolicy.forceMergeEnabledFieldLabel,
'data-test-subj': `${phase}-forceMergeSwitch`,
initialValue: initialToggleValue,
}}
>
<EuiSpacer />
<div id="forcemergeContent" aria-live="polite" role="region">
<UseField
key={`phases.${phase}.actions.forcemerge.max_num_segments`}
path={`phases.${phase}.actions.forcemerge.max_num_segments`}
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-selectedForceMergeSegments`,
min: 1,
},
}}
/>
<UseField
key={`_meta.${phase}.bestCompression`}
path={`_meta.${phase}.bestCompression`}
component={ToggleField}
componentProps={{
hasEmptyLabelSpace: true,
euiFieldProps: {
'data-test-subj': `${phase}-bestCompression`,
},
}}
/>
</div>
<UseField
path={`phases.${phase}.actions.forcemerge.max_num_segments`}
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-selectedForceMergeSegments`,
min: 1,
},
}}
/>
<UseField
path={`_meta.${phase}.bestCompression`}
component={CheckBoxField}
componentProps={{
hasEmptyLabelSpace: true,
euiFieldProps: {
'data-test-subj': `${phase}-bestCompression`,
},
}}
/>
</DescribedFormRow>
);
};

View file

@ -8,9 +8,7 @@ export { DataTierAllocationField } from './data_tier_allocation_field';
export { ForcemergeField } from './forcemerge_field';
export { SetPriorityInputField } from './set_priority_input_field';
export { MinAgeInputField } from './min_age_input_field';
export { MinAgeField } from './min_age_field';
export { SnapshotPoliciesField } from './snapshot_policies_field';
@ -19,3 +17,7 @@ export { ShrinkField } from './shrink_field';
export { SearchableSnapshotField } from './searchable_snapshot_field';
export { ReadonlyField } from './readonly_field';
export { ReplicasField } from './replicas_field';
export { IndexPriorityField } from './index_priority_field';

View file

@ -4,23 +4,32 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiTextColor, EuiDescribedFormGroup } from '@elastic/eui';
import { Phases } from '../../../../../../../common/types';
import { i18n } from '@kbn/i18n';
import { EuiSpacer, EuiTextColor } from '@elastic/eui';
import { UseField, NumericField } from '../../../../../../shared_imports';
import { LearnMoreLink } from '../..';
import { LearnMoreLink, DescribedFormRow } from '../..';
import { useEditPolicyContext } from '../../../edit_policy_context';
interface Props {
phase: keyof Phases & string;
phase: 'hot' | 'warm' | 'cold';
}
export const SetPriorityInputField: FunctionComponent<Props> = ({ phase }) => {
export const IndexPriorityField: FunctionComponent<Props> = ({ phase }) => {
const { policy, isNewPolicy } = useEditPolicyContext();
const initialToggleValue = useMemo<boolean>(() => {
return (
isNewPolicy || // enable index priority for new policies
!policy.phases[phase]?.actions || // enable index priority for new phases
policy.phases[phase]?.actions?.set_priority != null // enable index priority if it's set
);
}, [isNewPolicy, policy.phases, phase]);
return (
<EuiDescribedFormGroup
<DescribedFormRow
title={
<h3>
<FormattedMessage
@ -41,19 +50,27 @@ export const SetPriorityInputField: FunctionComponent<Props> = ({ phase }) => {
}
titleSize="xs"
fullWidth
switchProps={{
label: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.indexPriority.indexPriorityEnabledFieldLabel',
{
defaultMessage: 'Set index priority',
}
),
'data-test-subj': `${phase}-indexPrioritySwitch`,
initialValue: initialToggleValue,
}}
>
<EuiSpacer />
<UseField
key={`phases.${phase}.actions.set_priority.priority`}
path={`phases.${phase}.actions.set_priority.priority`}
component={NumericField}
componentProps={{
euiFieldProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-phaseIndexPriority`,
min: 0,
},
'data-test-subj': `${phase}-indexPriority`,
min: 0,
}}
/>
</EuiDescribedFormGroup>
</DescribedFormRow>
);
};

View file

@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export { MinAgeField } from './min_age_field';

View file

@ -0,0 +1,158 @@
/*
* 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, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFieldNumber,
EuiFieldNumberProps,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiSelect,
EuiText,
} from '@elastic/eui';
import { UseField, getFieldValidityAndErrorMessage } from '../../../../../../../shared_imports';
import { getUnitsAriaLabelForPhase, getTimingLabelForPhase } from './util';
type PhaseWithMinAgeAction = 'warm' | 'cold' | 'delete';
const i18nTexts = {
daysOptionLabel: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.daysOptionLabel', {
defaultMessage: 'days',
}),
hoursOptionLabel: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.hoursOptionLabel', {
defaultMessage: 'hours',
}),
minutesOptionLabel: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.minutesOptionLabel', {
defaultMessage: 'minutes',
}),
secondsOptionLabel: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.secondsOptionLabel', {
defaultMessage: 'seconds',
}),
millisecondsOptionLabel: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.milliSecondsOptionLabel',
{
defaultMessage: 'milliseconds',
}
),
microsecondsOptionLabel: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.microSecondsOptionLabel',
{
defaultMessage: 'microseconds',
}
),
nanosecondsOptionLabel: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.nanoSecondsOptionLabel',
{
defaultMessage: 'nanoseconds',
}
),
};
interface Props {
phase: PhaseWithMinAgeAction;
}
export const MinAgeField: FunctionComponent<Props> = ({ phase }): React.ReactElement => {
return (
<UseField path={`phases.${phase}.min_age`}>
{(field) => {
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
return (
<EuiFormRow fullWidth isInvalid={isInvalid} error={errorMessage}>
<EuiFlexGroup gutterSize={'s'} alignItems={'center'} justifyContent={'spaceBetween'}>
<EuiFlexItem grow={false}>
<EuiText className={'eui-textNoWrap'} size={'xs'}>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.minimumAge.minimumAgeFieldLabel"
defaultMessage="Move data into phase when:"
/>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiFlexGroup gutterSize={'s'}>
<EuiFlexItem grow={false}>
<EuiFieldNumber
style={{ minWidth: 50 }}
compressed
aria-label={getTimingLabelForPhase(phase)}
isInvalid={isInvalid}
value={field.value as EuiFieldNumberProps['value']}
onChange={field.onChange}
isLoading={field.isValidating}
data-test-subj={`${phase}-selectedMinimumAge`}
min={0}
/>
</EuiFlexItem>
<EuiFlexItem grow={true} style={{ minWidth: 165 }}>
<UseField path={`_meta.${phase}.minAgeUnit`}>
{(unitField) => {
const { isInvalid: isUnitFieldInvalid } = getFieldValidityAndErrorMessage(
unitField
);
return (
<EuiSelect
compressed
value={unitField.value as string}
onChange={(e) => {
unitField.setValue(e.target.value);
}}
isInvalid={isUnitFieldInvalid}
append={'old'}
data-test-subj={`${phase}-selectedMinimumAgeUnits`}
aria-label={getUnitsAriaLabelForPhase(phase)}
options={[
{
value: 'd',
text: i18nTexts.daysOptionLabel,
},
{
value: 'h',
text: i18nTexts.hoursOptionLabel,
},
{
value: 'm',
text: i18nTexts.minutesOptionLabel,
},
{
value: 's',
text: i18nTexts.secondsOptionLabel,
},
{
value: 'ms',
text: i18nTexts.millisecondsOptionLabel,
},
{
value: 'micros',
text: i18nTexts.microsecondsOptionLabel,
},
{
value: 'nanos',
text: i18nTexts.nanosecondsOptionLabel,
},
]}
/>
);
}}
</UseField>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFormRow>
);
}}
</UseField>
);
};

View file

@ -1,205 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { UseField, NumericField, SelectField } from '../../../../../../../shared_imports';
import { LearnMoreLink } from '../../../learn_more_link';
import { useConfigurationIssues } from '../../../../form';
import { getUnitsAriaLabelForPhase, getTimingLabelForPhase } from './util';
type PhaseWithMinAgeAction = 'warm' | 'cold' | 'delete';
interface Props {
phase: PhaseWithMinAgeAction;
}
export const MinAgeInputField: FunctionComponent<Props> = ({ phase }): React.ReactElement => {
const { isUsingRollover: rolloverEnabled } = useConfigurationIssues();
let daysOptionLabel;
let hoursOptionLabel;
let minutesOptionLabel;
let secondsOptionLabel;
let millisecondsOptionLabel;
let microsecondsOptionLabel;
let nanosecondsOptionLabel;
if (rolloverEnabled) {
daysOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverDaysOptionLabel',
{
defaultMessage: 'days from rollover',
}
);
hoursOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverHoursOptionLabel',
{
defaultMessage: 'hours from rollover',
}
);
minutesOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverMinutesOptionLabel',
{
defaultMessage: 'minutes from rollover',
}
);
secondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverSecondsOptionLabel',
{
defaultMessage: 'seconds from rollover',
}
);
millisecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverMilliSecondsOptionLabel',
{
defaultMessage: 'milliseconds from rollover',
}
);
microsecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverMicroSecondsOptionLabel',
{
defaultMessage: 'microseconds from rollover',
}
);
nanosecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.rolloverNanoSecondsOptionLabel',
{
defaultMessage: 'nanoseconds from rollover',
}
);
} else {
daysOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationDaysOptionLabel',
{
defaultMessage: 'days from index creation',
}
);
hoursOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationHoursOptionLabel',
{
defaultMessage: 'hours from index creation',
}
);
minutesOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationMinutesOptionLabel',
{
defaultMessage: 'minutes from index creation',
}
);
secondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationSecondsOptionLabel',
{
defaultMessage: 'seconds from index creation',
}
);
millisecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationMilliSecondsOptionLabel',
{
defaultMessage: 'milliseconds from index creation',
}
);
microsecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationMicroSecondsOptionLabel',
{
defaultMessage: 'microseconds from index creation',
}
);
nanosecondsOptionLabel = i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.creationNanoSecondsOptionLabel',
{
defaultMessage: 'nanoseconds from index creation',
}
);
}
return (
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: 140 }}>
<UseField
path={`phases.${phase}.min_age`}
component={NumericField}
componentProps={{
label: getTimingLabelForPhase(phase),
helpText: (
<LearnMoreLink
docPath="ilm-index-lifecycle.html#ilm-phase-transitions"
text={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.learnAboutTimingText"
defaultMessage="Learn about timing"
/>
}
/>
),
euiFieldProps: {
'data-test-subj': `${phase}-selectedMinimumAge`,
min: 0,
},
}}
/>
</EuiFlexItem>
<EuiFlexItem style={{ maxWidth: 236 }}>
<UseField
path={`_meta.${phase}.minAgeUnit`}
component={SelectField}
componentProps={{
hasEmptyLabelSpace: true,
euiFieldProps: {
'data-test-subj': `${phase}-selectedMinimumAgeUnits`,
'aria-label': getUnitsAriaLabelForPhase(phase),
options: [
{
value: 'd',
text: daysOptionLabel,
},
{
value: 'h',
text: hoursOptionLabel,
},
{
value: 'm',
text: minutesOptionLabel,
},
{
value: 's',
text: secondsOptionLabel,
},
{
value: 'ms',
text: millisecondsOptionLabel,
},
{
value: 'micros',
text: microsecondsOptionLabel,
},
{
value: 'nanos',
text: nanosecondsOptionLabel,
},
],
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
import { UseField, NumericField } from '../../../../../../shared_imports';
import { useEditPolicyContext } from '../../../edit_policy_context';
import { DescribedFormRow } from '../../described_form_row';
interface Props {
phase: 'warm' | 'cold';
}
export const ReplicasField: FunctionComponent<Props> = ({ phase }) => {
const { policy } = useEditPolicyContext();
const initialValue = policy.phases[phase]?.actions?.allocate?.number_of_replicas != null;
return (
<DescribedFormRow
title={
<h3>
{i18n.translate('xpack.indexLifecycleMgmt.numberOfReplicas.formRowTitle', {
defaultMessage: 'Replicas',
})}
</h3>
}
description={i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.numberOfReplicas.formRowDescription',
{
defaultMessage:
'Set the number of replicas. Remains the same as the previous phase by default.',
}
)}
switchProps={{
'data-test-subj': `${phase}-setReplicasSwitch`,
label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.numberOfReplicas.switchLabel', {
defaultMessage: 'Set replicas',
}),
initialValue,
}}
fullWidth
>
<UseField
path={`phases.${phase}.actions.allocate.number_of_replicas`}
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-selectedReplicaCount`,
min: 0,
},
}}
/>
</DescribedFormRow>
);
};

View file

@ -42,32 +42,28 @@ export const ShrinkField: FunctionComponent<Props> = ({ phase }) => {
}
titleSize="xs"
switchProps={{
'aria-controls': 'shrinkContent',
'data-test-subj': `${phase}-shrinkSwitch`,
label: i18nTexts.editPolicy.shrinkLabel,
'aria-label': i18nTexts.editPolicy.shrinkLabel,
initialValue: Boolean(policy.phases[phase]?.actions?.shrink),
}}
fullWidth
>
<div id="shrinkContent" aria-live="polite" role="region">
<EuiFlexGroup>
<EuiFlexItem>
<UseField
path={path}
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-selectedPrimaryShardCount`,
min: 1,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
</div>
<EuiFlexGroup>
<EuiFlexItem>
<UseField
path={path}
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${phase}-primaryShardCount`,
min: 1,
},
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
</DescribedFormRow>
);
};

View file

@ -5,30 +5,21 @@
*/
import React, { FunctionComponent } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import { EuiSpacer, EuiDescribedFormGroup, EuiAccordion } from '@elastic/eui';
import { useFormData, UseField, ToggleField, NumericField } from '../../../../../../shared_imports';
import { Phases } from '../../../../../../../common/types';
import { useEditPolicyContext } from '../../../edit_policy_context';
import { useConfigurationIssues } from '../../../form';
import { ActiveBadge, DescribedFormRow } from '../../';
import {
MinAgeInputField,
ForcemergeField,
SetPriorityInputField,
IndexPriorityField,
DataTierAllocationField,
ShrinkField,
ReadonlyField,
ReplicasField,
} from '../shared_fields';
import { Phase } from '../phase';
const i18nTexts = {
dataTierAllocation: {
description: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.description', {
@ -37,153 +28,26 @@ const i18nTexts = {
},
};
const warmProperty: keyof Phases = 'warm';
const formFieldPaths = {
enabled: '_meta.warm.enabled',
warmPhaseOnRollover: '_meta.warm.warmPhaseOnRollover',
};
export const WarmPhase: FunctionComponent = () => {
const { policy } = useEditPolicyContext();
const { isUsingSearchableSnapshotInHotPhase, isUsingRollover } = useConfigurationIssues();
const [formData] = useFormData({
watch: [formFieldPaths.enabled, formFieldPaths.warmPhaseOnRollover],
});
const enabled = get(formData, formFieldPaths.enabled);
const warmPhaseOnRollover = get(formData, formFieldPaths.warmPhaseOnRollover);
const { isUsingSearchableSnapshotInHotPhase } = useConfigurationIssues();
return (
<div id="warmPhaseContent" aria-live="polite" role="region" aria-relevant="additions">
<>
<EuiDescribedFormGroup
title={
<div>
<h2 className="eui-displayInlineBlock eui-alignMiddle">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseLabel"
defaultMessage="Warm phase"
/>
</h2>{' '}
{enabled && <ActiveBadge />}
</div>
}
titleSize="s"
description={
<>
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescriptionMessage"
defaultMessage="You are still querying your index, but it is read-only.
You can allocate shards to less performant hardware.
For faster searches, you can reduce the number of shards and force merge segments."
/>
</p>
<UseField
path={formFieldPaths.enabled}
component={ToggleField}
componentProps={{
euiFieldProps: {
'data-test-subj': 'enablePhaseSwitch-warm',
'aria-controls': 'warmPhaseContent',
},
}}
/>
</>
}
fullWidth
>
<>
{enabled && (
<>
{isUsingRollover && (
<UseField
path={formFieldPaths.warmPhaseOnRollover}
component={ToggleField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${warmProperty}-warmPhaseOnRollover`,
},
}}
/>
)}
{(!warmPhaseOnRollover || !isUsingRollover) && (
<>
<EuiSpacer size="m" />
<MinAgeInputField phase="warm" />
</>
)}
</>
)}
</>
</EuiDescribedFormGroup>
<Phase phase={'warm'}>
<ReplicasField phase={'warm'} />
{enabled && (
<EuiAccordion
id="ilmWarmPhaseAdvancedSettings"
buttonContent={i18n.translate(
'xpack.indexLifecycleMgmt.warmPhase.advancedSettingsButton',
{
defaultMessage: 'Advanced settings',
}
)}
paddingSize="m"
>
<DescribedFormRow
title={
<h3>
{i18n.translate('xpack.indexLifecycleMgmt.warmPhase.replicasTitle', {
defaultMessage: 'Replicas',
})}
</h3>
}
description={i18n.translate(
'xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasDescription',
{
defaultMessage:
'Set the number of replicas. Remains the same as the previous phase by default.',
}
)}
switchProps={{
'data-test-subj': 'warm-setReplicasSwitch',
label: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.warmPhase.numberOfReplicas.switchLabel',
{ defaultMessage: 'Set replicas' }
),
initialValue: policy.phases.warm?.actions?.allocate?.number_of_replicas != null,
}}
fullWidth
>
<UseField
path="phases.warm.actions.allocate.number_of_replicas"
component={NumericField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': `${warmProperty}-selectedReplicaCount`,
min: 0,
},
}}
/>
</DescribedFormRow>
{!isUsingSearchableSnapshotInHotPhase && <ShrinkField phase={'warm'} />}
{!isUsingSearchableSnapshotInHotPhase && <ShrinkField phase="warm" />}
{!isUsingSearchableSnapshotInHotPhase && <ForcemergeField phase={'warm'} />}
{!isUsingSearchableSnapshotInHotPhase && <ForcemergeField phase="warm" />}
<ReadonlyField phase={'warm'} />
<ReadonlyField phase={'warm'} />
{/* Data tier allocation section */}
<DataTierAllocationField
description={i18nTexts.dataTierAllocation.description}
phase={'warm'}
/>
{/* Data tier allocation section */}
<DataTierAllocationField
description={i18nTexts.dataTierAllocation.description}
phase={warmProperty}
/>
<SetPriorityInputField phase="warm" />
</EuiAccordion>
)}
</>
</div>
<IndexPriorityField phase={'warm'} />
</Phase>
);
};

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment, useEffect, useState, useMemo } from 'react';
import React, { Fragment, useEffect, useMemo, useState } from 'react';
import { get } from 'lodash';
import { RouteComponentProps } from 'react-router-dom';
@ -16,7 +16,6 @@ import { i18n } from '@kbn/i18n';
import {
EuiButton,
EuiButtonEmpty,
EuiDescribedFormGroup,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
@ -24,33 +23,35 @@ import {
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { useForm, UseField, TextField, useFormData } from '../../../shared_imports';
import { TextField, UseField, useForm, useFormData } from '../../../shared_imports';
import { toasts } from '../../services/notification';
import { savePolicy } from './save_policy';
import {
LearnMoreLink,
PolicyJsonFlyout,
ColdPhase,
DeletePhase,
HotPhase,
PolicyJsonFlyout,
WarmPhase,
Timeline,
} from './components';
import { schema, deserializer, createSerializer, createPolicyNameValidations, Form } from './form';
import { createPolicyNameValidations, createSerializer, deserializer, Form, schema } from './form';
import { useEditPolicyContext } from './edit_policy_context';
import { FormInternal } from './types';
import { createDocLink } from '../../services/documentation';
export interface Props {
history: RouteComponentProps['history'];
@ -75,7 +76,7 @@ export const EditPolicy: React.FunctionComponent<Props> = ({ history }) => {
return createSerializer(isNewPolicy ? undefined : currentPolicy);
}, [isNewPolicy, currentPolicy]);
const [saveAsNew, setSaveAsNew] = useState(isNewPolicy);
const [saveAsNew, setSaveAsNew] = useState(false);
const originalPolicyName: string = isNewPolicy ? '' : policyName!;
const { form } = useForm({
@ -116,7 +117,7 @@ export const EditPolicy: React.FunctionComponent<Props> = ({ history }) => {
);
} else {
const success = await savePolicy(
{ ...policy, name: saveAsNew ? currentPolicyName : originalPolicyName },
{ ...policy, name: saveAsNew || isNewPolicy ? currentPolicyName : originalPolicyName },
isNewPolicy || saveAsNew
);
if (success) {
@ -132,216 +133,187 @@ export const EditPolicy: React.FunctionComponent<Props> = ({ history }) => {
return (
<EuiPage>
<EuiPageBody>
<EuiPageContent
className="ilmEditPolicyPageContent"
verticalPosition="center"
horizontalPosition="center"
>
<EuiTitle size="l" data-test-subj="policyTitle">
<h1>
{isNewPolicy
? i18n.translate('xpack.indexLifecycleMgmt.editPolicy.createPolicyMessage', {
defaultMessage: 'Create an index lifecycle policy',
})
: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.editPolicyMessage', {
defaultMessage: 'Edit index lifecycle policy {originalPolicyName}',
values: { originalPolicyName },
})}
</h1>
</EuiTitle>
<div className="euiAnimateContentLoad">
<Form form={form}>
<EuiSpacer size="xs" />
<EuiText color="subdued">
<p>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.lifecyclePolicyDescriptionText"
defaultMessage="Use an index policy to automate the four phases of the index lifecycle,
from actively writing to the index to deleting it."
/>{' '}
<LearnMoreLink
docPath="index-lifecycle-management.html"
text={
<EuiPageContent>
<EuiPageContentHeader>
<EuiPageContentHeaderSection>
<EuiTitle size="l" data-test-subj="policyTitle">
<h1>
{isNewPolicy
? i18n.translate('xpack.indexLifecycleMgmt.editPolicy.createPolicyMessage', {
defaultMessage: 'Create Policy',
})
: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.editPolicyMessage', {
defaultMessage: 'Edit Policy {originalPolicyName}',
values: { originalPolicyName },
})}
</h1>
</EuiTitle>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiButtonEmpty
href={createDocLink('index-lifecycle-management.html')}
target="_blank"
iconType="help"
>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.documentationLinkText"
defaultMessage="Documentation"
/>
</EuiButtonEmpty>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<Form form={form}>
{isNewPolicy ? null : (
<Fragment>
<EuiText>
<p>
<strong>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexLifecycleManagementLinkText"
defaultMessage="Learn about the index lifecycle."
id="xpack.indexLifecycleMgmt.editPolicy.editingExistingPolicyMessage"
defaultMessage="You are editing an existing policy"
/>
}
/>
</p>
</EuiText>
<EuiSpacer />
{isNewPolicy ? null : (
<Fragment>
<EuiText>
<p>
<strong>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.editingExistingPolicyMessage"
defaultMessage="You are editing an existing policy"
/>
</strong>
.{' '}
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.editingExistingPolicyExplanationMessage"
defaultMessage="Any changes you make will affect the indices that are
</strong>
.{' '}
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.editingExistingPolicyExplanationMessage"
defaultMessage="Any changes you make will affect the indices that are
attached to this policy. Alternatively, you can save these changes in
a new policy."
/>
</p>
</EuiText>
<EuiSpacer />
<EuiFormRow>
<EuiSwitch
data-test-subj="saveAsNewSwitch"
style={{ maxWidth: '100%' }}
checked={saveAsNew}
onChange={(e) => {
setSaveAsNew(e.target.checked);
}}
label={
<span>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.saveAsNewPolicyMessage"
defaultMessage="Save as new policy"
/>
</span>
}
/>
</EuiFormRow>
</Fragment>
)}
</p>
</EuiText>
<EuiSpacer />
{saveAsNew || isNewPolicy ? (
<EuiDescribedFormGroup
title={
<div>
<span className="eui-displayInlineBlock eui-alignMiddle">
<EuiFormRow>
<EuiSwitch
data-test-subj="saveAsNewSwitch"
style={{ maxWidth: '100%' }}
checked={saveAsNew}
onChange={(e) => {
setSaveAsNew(e.target.checked);
}}
label={
<span>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.nameLabel"
defaultMessage="Name"
id="xpack.indexLifecycleMgmt.editPolicy.saveAsNewPolicyMessage"
defaultMessage="Save as new policy"
/>
</span>
</div>
}
titleSize="s"
fullWidth
>
<UseField<string, FormInternal>
path={policyNamePath}
config={{
label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.policyNameLabel', {
defaultMessage: 'Policy name',
}),
helpText: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage',
{
defaultMessage:
'A policy name cannot start with an underscore and cannot contain a comma or a space.',
}
),
validations: policyNameValidations,
}}
component={TextField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': 'policyNameField',
},
}}
}
/>
</EuiDescribedFormGroup>
) : null}
</EuiFormRow>
</Fragment>
)}
<EuiHorizontalRule />
{saveAsNew || isNewPolicy ? (
<UseField<string, FormInternal>
path={policyNamePath}
config={{
label: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.policyNameLabel', {
defaultMessage: 'Policy name',
}),
helpText: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage',
{
defaultMessage:
'A policy name cannot start with an underscore and cannot contain a comma or a space.',
}
),
validations: policyNameValidations,
}}
component={TextField}
componentProps={{
fullWidth: false,
euiFieldProps: {
'data-test-subj': 'policyNameField',
},
}}
/>
) : null}
<Timeline />
<EuiHorizontalRule />
<EuiSpacer size="l" />
<Timeline />
<HotPhase />
<EuiSpacer size="l" />
<EuiHorizontalRule />
<HotPhase />
<WarmPhase />
<EuiSpacer />
<EuiHorizontalRule />
<WarmPhase />
<ColdPhase />
<EuiSpacer />
<EuiHorizontalRule />
<ColdPhase />
<DeletePhase />
<EuiSpacer />
<EuiHorizontalRule />
<DeletePhase />
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="savePolicyButton"
fill
iconType="check"
iconSide="left"
disabled={form.isValid === false || form.isSubmitting}
onClick={submit}
color="secondary"
>
{saveAsNew ? (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.saveAsNewButton"
defaultMessage="Save as new policy"
/>
) : (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.saveButton"
defaultMessage="Save policy"
/>
)}
</EuiButton>
</EuiFlexItem>
<EuiHorizontalRule />
<EuiFlexItem grow={false}>
<EuiButtonEmpty data-test-subj="cancelTestPolicy" onClick={backToPolicyList}>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={togglePolicyJsonFlyout} data-test-subj="requestButton">
{isShowingPolicyJsonFlyout ? (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hidePolicyJsonButto"
defaultMessage="Hide request"
/>
) : (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.showPolicyJsonButto"
defaultMessage="Show request"
/>
)}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty data-test-subj="cancelTestPolicy" onClick={backToPolicyList}>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.cancelButton"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="savePolicyButton"
fill
iconType="check"
iconSide="left"
disabled={form.isValid === false || form.isSubmitting}
onClick={submit}
>
{saveAsNew ? (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.cancelButton"
defaultMessage="Cancel"
id="xpack.indexLifecycleMgmt.editPolicy.saveAsNewButton"
defaultMessage="Save as new policy"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
) : (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.saveButton"
defaultMessage="Save policy"
/>
)}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={togglePolicyJsonFlyout} data-test-subj="requestButton">
{isShowingPolicyJsonFlyout ? (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.hidePolicyJsonButto"
defaultMessage="Hide request"
/>
) : (
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.showPolicyJsonButto"
defaultMessage="Show request"
/>
)}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
{isShowingPolicyJsonFlyout ? (
<PolicyJsonFlyout
policyName={saveAsNew ? currentPolicyName : policyName}
close={() => setIsShowingPolicyJsonFlyout(false)}
/>
) : null}
</Form>
</div>
{isShowingPolicyJsonFlyout ? (
<PolicyJsonFlyout
policyName={saveAsNew ? currentPolicyName : policyName}
close={() => setIsShowingPolicyJsonFlyout(false)}
/>
) : null}
</Form>
</EuiPageContent>
</EuiPageBody>
</EuiPage>

View file

@ -260,15 +260,6 @@ describe('deserializer and serializer', () => {
expect(result.phases.hot!.actions.readonly).toBeUndefined();
});
it('removes min_age from warm when rollover is enabled', () => {
formInternal._meta.hot.customRollover.enabled = true;
formInternal._meta.warm.warmPhaseOnRollover = true;
const result = serializer(formInternal);
expect(result.phases.warm!.min_age).toBeUndefined();
});
it('adds default rollover configuration when enabled, but previously not configured', () => {
delete formInternal.phases.hot!.actions.rollover;
formInternal._meta.hot.isUsingDefaultRollover = true;

View file

@ -7,13 +7,13 @@
import { i18n } from '@kbn/i18n';
import { FormSchema, fieldValidators } from '../../../../shared_imports';
import { defaultSetPriority, defaultPhaseIndexPriority } from '../../../constants';
import { defaultIndexPriority } from '../../../constants';
import { ROLLOVER_FORM_PATHS } from '../constants';
const rolloverFormPaths = Object.values(ROLLOVER_FORM_PATHS);
import { FormInternal } from '../types';
const rolloverFormPaths = Object.values(ROLLOVER_FORM_PATHS);
import {
ifExistsNumberGreaterThanZero,
ifExistsNumberNonNegative,
@ -54,7 +54,6 @@ export const schema: FormSchema<FormInternal> = {
},
bestCompression: {
label: i18nTexts.editPolicy.bestCompressionFieldLabel,
helpText: i18nTexts.editPolicy.bestCompressionFieldHelpText,
},
readonlyEnabled: {
defaultValue: false,
@ -69,18 +68,11 @@ export const schema: FormSchema<FormInternal> = {
{ defaultMessage: 'Activate warm phase' }
),
},
warmPhaseOnRollover: {
defaultValue: true,
label: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.moveToWarmPhaseOnRolloverLabel', {
defaultMessage: 'Move to warm phase on rollover',
}),
},
minAgeUnit: {
defaultValue: 'ms',
},
bestCompression: {
label: i18nTexts.editPolicy.bestCompressionFieldLabel,
helpText: i18nTexts.editPolicy.bestCompressionFieldHelpText,
},
dataTierAllocationType: {
label: i18nTexts.editPolicy.allocationTypeOptionsFieldLabel,
@ -218,9 +210,14 @@ export const schema: FormSchema<FormInternal> = {
},
set_priority: {
priority: {
defaultValue: defaultSetPriority as any,
label: i18nTexts.editPolicy.setPriorityFieldLabel,
validations: [{ validator: ifExistsNumberNonNegative }],
defaultValue: defaultIndexPriority.hot as any,
label: i18nTexts.editPolicy.indexPriorityFieldLabel,
validations: [
{
validator: emptyField(i18nTexts.editPolicy.errors.numberRequired),
},
{ validator: ifExistsNumberNonNegative },
],
serializer: serializers.stringToNumber,
},
},
@ -239,9 +236,12 @@ export const schema: FormSchema<FormInternal> = {
allocate: {
number_of_replicas: {
label: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel', {
defaultMessage: 'Number of replicas (optional)',
defaultMessage: 'Number of replicas',
}),
validations: [
{
validator: emptyField(i18nTexts.editPolicy.errors.numberRequired),
},
{
validator: ifExistsNumberNonNegative,
},
@ -289,9 +289,14 @@ export const schema: FormSchema<FormInternal> = {
},
set_priority: {
priority: {
defaultValue: defaultPhaseIndexPriority as any,
label: i18nTexts.editPolicy.setPriorityFieldLabel,
validations: [{ validator: ifExistsNumberNonNegative }],
defaultValue: defaultIndexPriority.warm as any,
label: i18nTexts.editPolicy.indexPriorityFieldLabel,
validations: [
{
validator: emptyField(i18nTexts.editPolicy.errors.numberRequired),
},
{ validator: ifExistsNumberNonNegative },
],
serializer: serializers.stringToNumber,
},
},
@ -310,9 +315,12 @@ export const schema: FormSchema<FormInternal> = {
allocate: {
number_of_replicas: {
label: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel', {
defaultMessage: 'Number of replicas (optional)',
defaultMessage: 'Number of replicas',
}),
validations: [
{
validator: emptyField(i18nTexts.editPolicy.errors.numberRequired),
},
{
validator: ifExistsNumberNonNegative,
},
@ -322,9 +330,14 @@ export const schema: FormSchema<FormInternal> = {
},
set_priority: {
priority: {
defaultValue: '0' as any,
label: i18nTexts.editPolicy.setPriorityFieldLabel,
validations: [{ validator: ifExistsNumberNonNegative }],
defaultValue: defaultIndexPriority.cold as any,
label: i18nTexts.editPolicy.indexPriorityFieldLabel,
validations: [
{
validator: emptyField(i18nTexts.editPolicy.errors.numberRequired),
},
{ validator: ifExistsNumberNonNegative },
],
serializer: serializers.stringToNumber,
},
},

View file

@ -20,9 +20,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
): SerializedPolicy => {
const { _meta, ...updatedPolicy } = data;
if (!updatedPolicy.phases || !updatedPolicy.phases.hot) {
updatedPolicy.phases = { hot: { actions: {} } };
}
updatedPolicy.phases = { hot: { actions: {} }, ...updatedPolicy.phases };
return produce<SerializedPolicy>(originalPolicy ?? defaultPolicy, (draft) => {
// Copy over all updated fields
@ -32,7 +30,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
* Important shared values for serialization
*/
const isUsingRollover = Boolean(
_meta.hot.isUsingDefaultRollover || _meta.hot.customRollover.enabled
_meta.hot?.isUsingDefaultRollover || _meta.hot?.customRollover.enabled
);
// Next copy over all meta fields and delete any fields that have been removed
@ -53,7 +51,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
* HOT PHASE ROLLOVER
*/
if (isUsingRollover) {
if (_meta.hot.isUsingDefaultRollover) {
if (_meta.hot?.isUsingDefaultRollover) {
hotPhaseActions.rollover = cloneDeep(defaultRolloverAction);
} else {
// Rollover may not exist if editing an existing policy with initially no rollover configured
@ -63,7 +61,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
// We are using user-defined, custom rollover settings.
if (updatedPolicy.phases.hot!.actions.rollover?.max_age) {
hotPhaseActions.rollover.max_age = `${hotPhaseActions.rollover.max_age}${_meta.hot.customRollover.maxAgeUnit}`;
hotPhaseActions.rollover.max_age = `${hotPhaseActions.rollover.max_age}${_meta.hot?.customRollover.maxAgeUnit}`;
} else {
delete hotPhaseActions.rollover.max_age;
}
@ -73,7 +71,7 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
}
if (updatedPolicy.phases.hot!.actions.rollover?.max_size) {
hotPhaseActions.rollover.max_size = `${hotPhaseActions.rollover.max_size}${_meta.hot.customRollover.maxStorageSizeUnit}`;
hotPhaseActions.rollover.max_size = `${hotPhaseActions.rollover.max_size}${_meta.hot?.customRollover.maxStorageSizeUnit}`;
} else {
delete hotPhaseActions.rollover.max_size;
}
@ -84,20 +82,20 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
*/
if (!updatedPolicy.phases.hot!.actions?.forcemerge) {
delete hotPhaseActions.forcemerge;
} else if (_meta.hot.bestCompression) {
} else if (_meta.hot?.bestCompression) {
hotPhaseActions.forcemerge!.index_codec = 'best_compression';
} else {
delete hotPhaseActions.forcemerge!.index_codec;
}
if (_meta.hot.bestCompression && hotPhaseActions.forcemerge) {
if (_meta.hot?.bestCompression && hotPhaseActions.forcemerge) {
hotPhaseActions.forcemerge.index_codec = 'best_compression';
}
/**
* HOT PHASE READ-ONLY
*/
if (_meta.hot.readonlyEnabled) {
if (_meta.hot?.readonlyEnabled) {
hotPhaseActions.readonly = hotPhaseActions.readonly ?? {};
} else {
delete hotPhaseActions.readonly;
@ -140,17 +138,9 @@ export const createSerializer = (originalPolicy?: SerializedPolicy) => (
/**
* WARM PHASE MIN AGE
*
* If warm phase on rollover is enabled, delete min age field
* An index lifecycle switches to warm phase when rollover occurs, so you cannot specify a warm phase time
* They are mutually exclusive
*/
if (
(!isUsingRollover || !_meta.warm.warmPhaseOnRollover) &&
updatedPolicy.phases.warm?.min_age
) {
if (updatedPolicy.phases.warm?.min_age) {
warmPhase.min_age = `${updatedPolicy.phases.warm!.min_age}${_meta.warm.minAgeUnit}`;
} else {
delete warmPhase.min_age;
}
/**

View file

@ -40,10 +40,10 @@ export const i18nTexts = {
defaultMessage: 'Number of segments',
}
),
setPriorityFieldLabel: i18n.translate(
indexPriorityFieldLabel: i18n.translate(
'xpack.indexLifecycleMgmt.editPolicy.indexPriorityLabel',
{
defaultMessage: 'Index priority (optional)',
defaultMessage: 'Index priority',
}
),
bestCompressionFieldLabel: i18n.translate(
@ -170,5 +170,30 @@ export const i18nTexts = {
}
),
},
titles: {
hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseTitle', {
defaultMessage: 'Hot phase',
}),
warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseTitle', {
defaultMessage: 'Warm phase',
}),
cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle', {
defaultMessage: 'Cold phase',
}),
},
descriptions: {
hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseDescription', {
defaultMessage:
'This phase is required. You are actively querying and writing to your index. For faster updates, you can roll over the index when it gets too big or too old.',
}),
warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescription', {
defaultMessage:
'You are still querying your index, but it is read-only. You can allocate shards to less performant hardware. For faster searches, you can reduce the number of shards and force merge segments.',
}),
cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription', {
defaultMessage:
'You are querying your index less frequently, so you can allocate shards on significantly less performant hardware. Because your queries are slower, you can reduce the number of replicas.',
}),
},
},
};

View file

@ -70,9 +70,10 @@ export interface PhaseAgeInMilliseconds {
const phaseOrder: Phase[] = ['hot', 'warm', 'cold', 'delete'];
const getMinAge = (phase: MinAgePhase, formData: FormInternal) => ({
min_age: formData.phases[phase]?.min_age
? formData.phases[phase]!.min_age! + formData._meta[phase].minAgeUnit
: '0ms',
min_age:
formData.phases && formData.phases[phase]?.min_age
? formData.phases[phase]!.min_age! + formData._meta[phase].minAgeUnit
: '0ms',
});
const formDataToAbsoluteTimings = (formData: FormInternal): AbsoluteTimings => {

View file

@ -9,7 +9,7 @@ import {
UIM_CONFIG_WARM_PHASE,
UIM_CONFIG_SET_PRIORITY,
UIM_CONFIG_FREEZE_INDEX,
defaultPhaseIndexPriority,
defaultIndexPriority,
} from '../constants/';
import { getUiMetricsForPhases } from './ui_metric';
@ -22,7 +22,7 @@ describe('getUiMetricsForPhases', () => {
min_age: '0ms',
actions: {
set_priority: {
priority: parseInt(defaultPhaseIndexPriority, 10),
priority: parseInt(defaultIndexPriority.cold, 10),
},
},
},
@ -37,7 +37,7 @@ describe('getUiMetricsForPhases', () => {
min_age: '0ms',
actions: {
set_priority: {
priority: parseInt(defaultPhaseIndexPriority, 10),
priority: parseInt(defaultIndexPriority.warm, 10),
},
},
},
@ -52,7 +52,7 @@ describe('getUiMetricsForPhases', () => {
min_age: '0ms',
actions: {
set_priority: {
priority: parseInt(defaultPhaseIndexPriority, 10) + 1,
priority: parseInt(defaultIndexPriority.warm, 10) + 1,
},
},
},
@ -68,7 +68,7 @@ describe('getUiMetricsForPhases', () => {
actions: {
freeze: {},
set_priority: {
priority: parseInt(defaultPhaseIndexPriority, 10),
priority: parseInt(defaultIndexPriority.cold, 10),
},
},
},

View file

@ -19,8 +19,7 @@ import {
UIM_CONFIG_FREEZE_INDEX,
UIM_CONFIG_SET_PRIORITY,
UIM_CONFIG_WARM_PHASE,
defaultSetPriority,
defaultPhaseIndexPriority,
defaultIndexPriority,
} from '../constants';
import { Phases } from '../../../common/types';
@ -50,17 +49,17 @@ export function getUiMetricsForPhases(phases: Phases): string[] {
const isHotPhasePriorityChanged =
phases.hot &&
phases.hot.actions.set_priority &&
phases.hot.actions.set_priority.priority !== parseInt(defaultSetPriority, 10);
phases.hot.actions.set_priority.priority !== parseInt(defaultIndexPriority.hot, 10);
const isWarmPhasePriorityChanged =
phases.warm &&
phases.warm.actions.set_priority &&
phases.warm.actions.set_priority.priority !== parseInt(defaultPhaseIndexPriority, 10);
phases.warm.actions.set_priority.priority !== parseInt(defaultIndexPriority.warm, 10);
const isColdPhasePriorityChanged =
phases.cold &&
phases.cold.actions.set_priority &&
phases.cold.actions.set_priority.priority !== parseInt(defaultPhaseIndexPriority, 10);
phases.cold.actions.set_priority.priority !== parseInt(defaultIndexPriority.cold, 10);
// If the priority is different than the default, we'll consider it a user interaction,
// even if the user has set it to undefined.
return (

View file

@ -31,6 +31,7 @@ export {
SuperSelectField,
ComboBoxField,
TextField,
CheckBoxField,
} from '../../../../src/plugins/es_ui_shared/static/forms/components';
export { attemptToURIDecode } from '../../../../src/plugins/es_ui_shared/public';

View file

@ -9416,9 +9416,7 @@
"xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "コールドティアに割り当てられているノードがありません",
"xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。安価なハードウェアのコールドフェーズにデータを格納します。",
"xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "インデックスを凍結",
"xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasDescription": "レプリカの数を設定します。デフォルトでは前のフェーズと同じです。",
"xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel": "レプリカ数(任意)",
"xpack.indexLifecycleMgmt.coldPhase.replicasTitle": "レプリカ",
"xpack.indexLifecycleMgmt.common.dataTier.title": "データ割り当て",
"xpack.indexLifecycleMgmt.confirmDelete.cancelButton": "キャンセル",
"xpack.indexLifecycleMgmt.confirmDelete.deleteButton": "削除",
@ -9431,11 +9429,8 @@
"xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "コールドティアを作成",
"xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "コールドフェーズを有効にする",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescriptionText": "インデックスへのクエリの頻度を減らすことで、大幅に性能が低いハードウェアにシャードを割り当てることができます。クエリが遅いため、複製の数を減らすことができます。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseLabel": "コールドフェーズ",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText": "インデックスを読み取り専用にし、メモリー消費量を最小化します。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText": "凍結",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.numberOfReplicas.switchLabel": "レプリカを設定",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.helpText": "ノード属性に基づいてデータを移動します。",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.input": "カスタム",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.helpText": "コールドティアのノードにデータを移動します。",
@ -9452,13 +9447,6 @@
"xpack.indexLifecycleMgmt.editPolicy.createPolicyMessage": "インデックスライフサイクルポリシーを作成します",
"xpack.indexLifecycleMgmt.editPolicy.createSearchableSnapshotLink": "スナップショットリポジドリを作成",
"xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "新しいスナップショットリポジドリを作成",
"xpack.indexLifecycleMgmt.editPolicy.creationDaysOptionLabel": "インデックスの作成からの経過日数",
"xpack.indexLifecycleMgmt.editPolicy.creationHoursOptionLabel": "インデックスの作成からの経過時間数",
"xpack.indexLifecycleMgmt.editPolicy.creationMicroSecondsOptionLabel": "インデックスの作成からの経過時間(マイクロ秒)",
"xpack.indexLifecycleMgmt.editPolicy.creationMilliSecondsOptionLabel": "インデックスの作成からの経過時間(ミリ秒)",
"xpack.indexLifecycleMgmt.editPolicy.creationMinutesOptionLabel": "インデックスの作成からの経過時間(分)",
"xpack.indexLifecycleMgmt.editPolicy.creationNanoSecondsOptionLabel": "インデックスの作成からの経過時間(ナノ秒)",
"xpack.indexLifecycleMgmt.editPolicy.creationSecondsOptionLabel": "インデックスの作成からの経過時間(秒)",
"xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "データティアオプション",
"xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "ノード属性を選択",
"xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "コールド",
@ -9499,23 +9487,17 @@
"xpack.indexLifecycleMgmt.editPolicy.formErrorsMessage": "このページのエラーを修正してください。",
"xpack.indexLifecycleMgmt.editPolicy.hidePolicyJsonButto": "リクエストを非表示",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.enableRolloverTipContent": "現在のインデックスが定義された条件のいずれかを満たすときに、新しいインデックスにロールオーバーします。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseDescriptionMessage": "このフェーズは必須です。アクティブにクエリを実行しインデックスに書き込んでいます。 更新を高速化するため、大きくなりすぎたり古くなりすぎたりした際にインデックスをロールオーバーできます。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseLabel": "ホットフェーズ",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.learnAboutRolloverLinkText": "詳細",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDefaultsTipContent": "インデックスが 30 日経過するか、50 GB に達したらロールオーバーします。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDescriptionMessage": "効率的なストレージと高いパフォーマンスのための時系列データの自動ロールアウト。",
"xpack.indexLifecycleMgmt.editPolicy.indexPriorityLabel": "インデックス優先度(任意)",
"xpack.indexLifecycleMgmt.editPolicy.indexPriorityText": "インデックスの優先順位",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexLifecycleManagementLinkText": "インデックスライフサイクルの詳細をご覧ください。",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexTemplatesLink": "インデックステンプレートの詳細をご覧ください",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutShardAllocationLink": "シャード割り当ての詳細をご覧ください",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutTimingText": "タイミングの詳細をご覧ください",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesLoadingFailedTitle": "既存のライフサイクルポリシーを読み込めません",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesReloadButton": "再試行",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePolicyDescriptionText": "インデックスへのアクティブな書き込みから削除までの、インデックスライフサイクルの 4 つのフェーズを自動化するには、インデックスポリシーを使用します。",
"xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorBody": "このフィールドを更新し、既存のスナップショットリポジトリの名前を入力します。",
"xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorTitle": "スナップショットリポジトリを読み込めません",
"xpack.indexLifecycleMgmt.editPolicy.nameLabel": "名前",
"xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "ノード属性を使用して、シャード割り当てを制御します。{learnMoreLink}。",
"xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "割り当て構成を修正しない",
"xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "ノードデータを読み込めません",
@ -9542,13 +9524,6 @@
"xpack.indexLifecycleMgmt.editPolicy.readonlyDescription": "有効にすると、インデックスおよびインデックスメタデータを読み取り専用にします。無効にすると、書き込みとメタデータの変更を許可します。",
"xpack.indexLifecycleMgmt.editPolicy.readonlyTitle": "読み取り専用",
"xpack.indexLifecycleMgmt.editPolicy.reloadSnapshotRepositoriesLabel": "スナップショットリポジドリの再読み込み",
"xpack.indexLifecycleMgmt.editPolicy.rolloverDaysOptionLabel": "ロールオーバーからの経過日数",
"xpack.indexLifecycleMgmt.editPolicy.rolloverHoursOptionLabel": "ロールオーバーからの経過時間数",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMicroSecondsOptionLabel": "ロールオーバーからの経過時間(マイクロ秒)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMilliSecondsOptionLabel": "ロールオーバーからの経過時間(ミリ秒)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMinutesOptionLabel": "ロールオーバーからの経過時間数(分)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverNanoSecondsOptionLabel": "ロールオーバーからの経過時間(ナノ秒)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverSecondsOptionLabel": "ロールオーバーからの経過時間(秒)",
"xpack.indexLifecycleMgmt.editPolicy.saveAsNewButton": "新規ポリシーとして保存します",
"xpack.indexLifecycleMgmt.editPolicy.saveAsNewPolicyMessage": "新規ポリシーとして保存します",
"xpack.indexLifecycleMgmt.editPolicy.saveButton": "ポリシーを保存",
@ -9573,15 +9548,11 @@
"xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "ウォームフェーズを有効にする",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "ノードの再起動後にインデックスを復元する優先順位を設定します。優先順位の高いインデックスは優先順位の低いインデックスよりも先に復元されます。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.numberOfReplicas.switchLabel": "レプリカを設定",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescriptionMessage": "まだインデックスにクエリを実行中ですが、読み取り専用です。性能の低いハードウェアにシャードを割り当てることができます。検索を高速化するために、シャードの数を減らしセグメントを結合することができます。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseLabel": "ウォームフェーズ",
"xpack.indexLifecycleMgmt.featureCatalogueDescription": "ライフサイクルポリシーを定義し、インデックス年齢として自動的に処理を実行します。",
"xpack.indexLifecycleMgmt.featureCatalogueTitle": "インデックスライフサイクルを管理",
"xpack.indexLifecycleMgmt.forcemerge.bestCompressionLabel": "格納されたフィールドを圧縮",
"xpack.indexLifecycleMgmt.forcemerge.enableLabel": "データを強制結合",
"xpack.indexLifecycleMgmt.forceMerge.numberOfSegmentsLabel": "セグメントの数",
"xpack.indexLifecycleMgmt.hotPhase.advancedSettingsButton": "高度な設定",
"xpack.indexLifecycleMgmt.hotPhase.bytesLabel": "バイト",
"xpack.indexLifecycleMgmt.hotPhase.daysLabel": "日",
"xpack.indexLifecycleMgmt.hotPhase.enableRolloverLabel": "ロールオーバーを有効にする",
@ -9705,7 +9676,6 @@
"xpack.indexLifecycleMgmt.shrink.indexFieldLabel": "インデックスを縮小",
"xpack.indexLifecycleMgmt.shrink.numberOfPrimaryShardsLabel": "プライマリシャードの数",
"xpack.indexLifecycleMgmt.templateNotFoundMessage": "テンプレート{name}が見つかりません。",
"xpack.indexLifecycleMgmt.warmPhase.advancedSettingsButton": "高度な設定",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "ロールに基づく割り当てを使用するには、1つ以上のードを、ウォームまたはホットティアに割り当てます。使用可能なードがない場合、ポリシーは割り当てを完了できません。",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "ウォームティアに割り当てられているノードがありません",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "このポリシーはコールドフェーズのデータを{tier}ティアノードに移動します。",
@ -9713,10 +9683,7 @@
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "このポリシーはウォームフェーズのデータを{tier}ティアノードに移動します。",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "ウォームティアに割り当てられているノードがありません",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。",
"xpack.indexLifecycleMgmt.warmPhase.moveToWarmPhaseOnRolloverLabel": "ロールオーバー時にウォームフェーズに変更",
"xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasDescription": "レプリカの数を設定します。デフォルトでは前のフェーズと同じです。",
"xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel": "レプリカ数(任意)",
"xpack.indexLifecycleMgmt.warmPhase.replicasTitle": "レプリカ",
"xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし(グループなし)",
"xpack.infra.alerting.alertFlyout.groupByLabel": "グループ分けの条件",
"xpack.infra.alerting.alertsButton": "アラート",

View file

@ -9440,9 +9440,7 @@
"xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到冷层的节点",
"xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。将处于冷阶段的数据存储在成本较低的硬件上。",
"xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "冻结索引",
"xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasDescription": "设置副本数目。默认情况下仍与上一阶段相同。",
"xpack.indexLifecycleMgmt.coldPhase.numberOfReplicasLabel": "副本分片数(可选)",
"xpack.indexLifecycleMgmt.coldPhase.replicasTitle": "副本分片",
"xpack.indexLifecycleMgmt.common.dataTier.title": "数据分配",
"xpack.indexLifecycleMgmt.confirmDelete.cancelButton": "取消",
"xpack.indexLifecycleMgmt.confirmDelete.deleteButton": "删除",
@ -9455,11 +9453,8 @@
"xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "创建冷层",
"xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "激活冷阶段",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescriptionText": "您查询自己索引的频率较低,因此您可以在效率较低的硬件上分配分片。因为您的查询较为缓慢,所以您可以减少副本分片数目。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseLabel": "冷阶段",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeIndexExplanationText": "使索引只读,并最大限度减小其内存占用。",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.freezeText": "冻结",
"xpack.indexLifecycleMgmt.editPolicy.coldPhase.numberOfReplicas.switchLabel": "设置副本",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.helpText": "根据节点属性移动数据。",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.customOption.input": "定制",
"xpack.indexLifecycleMgmt.editPolicy.common.dataTierAllocation.cold.defaultOption.helpText": "将数据移到冷层中的节点。",
@ -9476,13 +9471,6 @@
"xpack.indexLifecycleMgmt.editPolicy.createPolicyMessage": "创建索引生命周期策略",
"xpack.indexLifecycleMgmt.editPolicy.createSearchableSnapshotLink": "创建快照库",
"xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "创建新的快照库",
"xpack.indexLifecycleMgmt.editPolicy.creationDaysOptionLabel": "天(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationHoursOptionLabel": "小时(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationMicroSecondsOptionLabel": "微秒(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationMilliSecondsOptionLabel": "毫秒(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationMinutesOptionLabel": "分钟(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationNanoSecondsOptionLabel": "纳秒(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.creationSecondsOptionLabel": "秒(自索引创建)",
"xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "数据层选项",
"xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "选择节点属性",
"xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "冷",
@ -9523,23 +9511,17 @@
"xpack.indexLifecycleMgmt.editPolicy.formErrorsMessage": "请修复此页面上的错误。",
"xpack.indexLifecycleMgmt.editPolicy.hidePolicyJsonButto": "隐藏请求",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.enableRolloverTipContent": "在当前索引满足定义的条件之一时,滚动更新到新索引。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseDescriptionMessage": "此阶段为必需。您正频繁地查询并写到您的索引。 为了获取更快的更新,在索引变得过大或过旧时,您可以滚动更新索引。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.hotPhaseLabel": "热阶段",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.learnAboutRolloverLinkText": "了解详情",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDefaultsTipContent": "当索引已存在 30 天或达到 50 GB 时滚动更新。",
"xpack.indexLifecycleMgmt.editPolicy.hotPhase.rolloverDescriptionMessage": "自动滚动更新时间序列数据,以实现高效存储和更高性能。",
"xpack.indexLifecycleMgmt.editPolicy.indexPriorityLabel": "索引优先级(可选)",
"xpack.indexLifecycleMgmt.editPolicy.indexPriorityText": "索引优先级",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexLifecycleManagementLinkText": "了解索引生命周期。",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutIndexTemplatesLink": "了解索引模板",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutShardAllocationLink": "了解分片分配",
"xpack.indexLifecycleMgmt.editPolicy.learnAboutTimingText": "了解计时",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesLoadingFailedTitle": "无法加载现有生命周期策略",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesReloadButton": "重试",
"xpack.indexLifecycleMgmt.editPolicy.lifecyclePolicyDescriptionText": "使用索引策略自动化索引生命周期的四个阶段,从频繁地写入到索引到删除索引。",
"xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorBody": "刷新此字段并输入现有快照储存库的名称。",
"xpack.indexLifecycleMgmt.editPolicy.loadSnapshotRepositoriesErrorTitle": "无法加载快照存储库",
"xpack.indexLifecycleMgmt.editPolicy.nameLabel": "名称",
"xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "使用节点属性控制分片分配。{learnMoreLink}。",
"xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "切勿修改分配配置",
"xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "无法加载节点数据",
@ -9566,13 +9548,6 @@
"xpack.indexLifecycleMgmt.editPolicy.readonlyDescription": "启用以使索引及索引元数据只读;禁用以允许写入和元数据更改。",
"xpack.indexLifecycleMgmt.editPolicy.readonlyTitle": "只读",
"xpack.indexLifecycleMgmt.editPolicy.reloadSnapshotRepositoriesLabel": "重新加载快照存储库",
"xpack.indexLifecycleMgmt.editPolicy.rolloverDaysOptionLabel": "天(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverHoursOptionLabel": "小时(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMicroSecondsOptionLabel": "微秒(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMilliSecondsOptionLabel": "毫秒(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverMinutesOptionLabel": "分钟(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverNanoSecondsOptionLabel": "纳秒(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.rolloverSecondsOptionLabel": "秒(自滚动更新)",
"xpack.indexLifecycleMgmt.editPolicy.saveAsNewButton": "另存为新策略",
"xpack.indexLifecycleMgmt.editPolicy.saveAsNewPolicyMessage": "另存为新策略",
"xpack.indexLifecycleMgmt.editPolicy.saveButton": "保存策略",
@ -9597,15 +9572,11 @@
"xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "激活温阶段",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "设置在节点重新启动后恢复索引的优先级。较高优先级的索引会在较低优先级的索引之前恢复。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.numberOfReplicas.switchLabel": "设置副本",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescriptionMessage": "您仍在查询自己的索引,但其为只读。您可以将分片分配给效率较低的硬件。为了获取更快的搜索,您可以减少分片数目并强制合并段。",
"xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseLabel": "温阶段",
"xpack.indexLifecycleMgmt.featureCatalogueDescription": "定义生命周期策略,以随着索引老化自动执行操作。",
"xpack.indexLifecycleMgmt.featureCatalogueTitle": "管理索引生命周期",
"xpack.indexLifecycleMgmt.forcemerge.bestCompressionLabel": "压缩已存储字段",
"xpack.indexLifecycleMgmt.forcemerge.enableLabel": "强制合并数据",
"xpack.indexLifecycleMgmt.forceMerge.numberOfSegmentsLabel": "分段数目",
"xpack.indexLifecycleMgmt.hotPhase.advancedSettingsButton": "高级设置",
"xpack.indexLifecycleMgmt.hotPhase.bytesLabel": "字节",
"xpack.indexLifecycleMgmt.hotPhase.daysLabel": "天",
"xpack.indexLifecycleMgmt.hotPhase.enableRolloverLabel": "启用滚动更新",
@ -9731,7 +9702,6 @@
"xpack.indexLifecycleMgmt.shrink.indexFieldLabel": "缩小索引",
"xpack.indexLifecycleMgmt.shrink.numberOfPrimaryShardsLabel": "主分片数目",
"xpack.indexLifecycleMgmt.templateNotFoundMessage": "找不到模板 {name}。",
"xpack.indexLifecycleMgmt.warmPhase.advancedSettingsButton": "高级设置",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "至少将一个节点分配到温层或冷层,以使用基于角色的分配。如果没有可用节点,则策略无法完成分配。",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到温层的节点",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "此策略会改为将冷阶段的数据移到{tier}层节点。",
@ -9739,10 +9709,7 @@
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "此策略会改为将温阶段的数据移到{tier}层节点。",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "没有分配到温层的节点",
"xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。",
"xpack.indexLifecycleMgmt.warmPhase.moveToWarmPhaseOnRolloverLabel": "滚动更新时移到温阶段",
"xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasDescription": "设置副本数目。默认情况下仍与上一阶段相同。",
"xpack.indexLifecycleMgmt.warmPhase.numberOfReplicasLabel": "副本分片数(可选)",
"xpack.indexLifecycleMgmt.warmPhase.replicasTitle": "副本分片",
"xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容(未分组)",
"xpack.infra.alerting.alertFlyout.groupByLabel": "分组依据",
"xpack.infra.alerting.alertsButton": "告警",