Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/components/PDiskInfo/PDiskInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {Flex} from '@gravity-ui/uikit';

import {getPDiskPagePath} from '../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {valueIsDefined} from '../../utils';
import {formatBytes} from '../../utils/bytesParsers';
import {cn} from '../../utils/cn';
Expand Down Expand Up @@ -191,12 +190,11 @@ export function PDiskInfo<T extends PreparedPDisk>({
className,
}: PDiskInfoProps<T>) {
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
const diskPagesAvailable = useDiskPagesAvailable();

const [generalInfo, statusInfo, spaceInfo, additionalInfo] = getPDiskInfo({
pDisk,
nodeId,
withPDiskPageLink: withPDiskPageLink && diskPagesAvailable,
withPDiskPageLink,
isUserAllowedToMakeChanges,
});

Expand Down
4 changes: 1 addition & 3 deletions src/components/VDiskInfo/VDiskInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';

import {getVDiskPagePath} from '../../routes';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {valueIsDefined} from '../../utils';
import {cn} from '../../utils/cn';
import {formatStorageValuesToGb, stringifyVdiskId} from '../../utils/dataFormatters/dataFormatters';
Expand Down Expand Up @@ -36,7 +35,6 @@ export function VDiskInfo<T extends PreparedVDisk>({
withTitle,
...infoViewerProps
}: VDiskInfoProps<T>) {
const diskPagesAvailable = useDiskPagesAvailable();
const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);

const {
Expand Down Expand Up @@ -154,7 +152,7 @@ export function VDiskInfo<T extends PreparedVDisk>({
if (diskParamsDefined) {
const links: React.ReactNode[] = [];

if (withVDiskPageLink && diskPagesAvailable) {
if (withVDiskPageLink) {
const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId);
links.push(
<LinkWithIcon
Expand Down
21 changes: 17 additions & 4 deletions src/containers/Node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {ResponseError} from '../../components/Errors/ResponseError';
import {FullNodeViewer} from '../../components/FullNodeViewer/FullNodeViewer';
import {Loader} from '../../components/Loader';
import routes, {createHref, parseQuery} from '../../routes';
import {
useCapabilitiesLoaded,
useDiskPagesAvailable,
} from '../../store/reducers/capabilities/hooks';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {nodeApi} from '../../store/reducers/node/node';
import type {AdditionalNodesProps} from '../../types/additionalProps';
Expand All @@ -18,7 +22,8 @@ import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
import {StorageWrapper} from '../Storage/StorageWrapper';
import {Tablets} from '../Tablets';

import {NODE_PAGES, OVERVIEW, STORAGE, TABLETS} from './NodePages';
import {NODE_PAGES, OVERVIEW, STORAGE, STRUCTURE, TABLETS} from './NodePages';
import NodeStructure from './NodeStructure/NodeStructure';

import './Node.scss';

Expand Down Expand Up @@ -50,11 +55,16 @@ export function Node(props: NodeProps) {
);
const loading = isFetching && currentData === undefined;
const node = currentData;
const capabilitiesLoaded = useCapabilitiesLoaded();
const isDiskPagesAvailable = useDiskPagesAvailable();

const {activeTabVerified, nodeTabs} = React.useMemo(() => {
const hasStorage = node?.Roles?.find((el) => el === STORAGE_ROLE);

const nodePages = hasStorage ? NODE_PAGES : NODE_PAGES.filter((el) => el.id !== STORAGE);
let nodePages = hasStorage ? NODE_PAGES : NODE_PAGES.filter((el) => el.id !== STORAGE);
if (isDiskPagesAvailable) {
nodePages = nodePages.filter((el) => el.id !== STRUCTURE);
}

const actualNodeTabs = nodePages.map((page) => {
return {
Expand All @@ -69,7 +79,7 @@ export function Node(props: NodeProps) {
}

return {activeTabVerified: actualActiveTab, nodeTabs: actualNodeTabs};
}, [activeTab, node]);
}, [activeTab, node, isDiskPagesAvailable]);

const tenantName = node?.Tenants?.[0] || tenantNameFromQuery?.toString();

Expand Down Expand Up @@ -131,6 +141,9 @@ export function Node(props: NodeProps) {
);
}

case STRUCTURE: {
return <NodeStructure className={b('node-page-wrapper')} nodeId={nodeId} />;
}
case OVERVIEW: {
return <FullNodeViewer node={node} className={b('overview-wrapper')} />;
}
Expand All @@ -140,7 +153,7 @@ export function Node(props: NodeProps) {
}
};

if (loading) {
if (loading || !capabilitiesLoaded) {
return <Loader size="l" />;
}

Expand Down
9 changes: 4 additions & 5 deletions src/containers/Node/NodePages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ export const NODE_PAGES = [
id: STORAGE,
name: 'Storage',
},
// TODO: remove Node Structure component
// {
// id: STRUCTURE,
// name: 'Structure',
// },
{
id: STRUCTURE,
name: 'Structure',
},
{
id: TABLETS,
name: 'Tablets',
Expand Down
22 changes: 14 additions & 8 deletions src/containers/PDiskPage/PDiskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
import {getPDiskPagePath} from '../../routes';
import {api} from '../../store/reducers/api';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {pDiskApi} from '../../store/reducers/pdisk/pdisk';
import type {EDecommitStatus} from '../../types/api/pdisk';
Expand Down Expand Up @@ -61,6 +62,7 @@ export function PDiskPage() {
const dispatch = useTypedDispatch();

const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
const newDiskApiAvailable = useDiskPagesAvailable();

const [{nodeId, pDiskId, activeTab}] = useQueryParams({
activeTab: StringParam,
Expand All @@ -87,7 +89,9 @@ export function PDiskPage() {

const handleRestart = async (isRetry?: boolean) => {
if (pDiskParamsDefined) {
const response = await window.api.restartPDisk({nodeId, pDiskId, force: isRetry});
const response = await window.api[
newDiskApiAvailable ? 'restartPDisk' : 'restartPDiskOld'
]({nodeId, pDiskId, force: isRetry});

if (response?.result === false) {
const err = {
Expand Down Expand Up @@ -188,13 +192,15 @@ export function PDiskPage() {
<Icon data={ArrowRotateLeft} />
{pDiskPageKeyset('restart-pdisk-button')}
</ButtonWithConfirmDialog>
<DecommissionButton
decommission={DecommitStatus}
onConfirmAction={handleDecommissionChange}
onConfirmActionSuccess={handleAfterAction}
buttonDisabled={!pDiskParamsDefined || !isUserAllowedToMakeChanges}
popoverDisabled={isUserAllowedToMakeChanges}
/>
{newDiskApiAvailable ? (
<DecommissionButton
decommission={DecommitStatus}
onConfirmAction={handleDecommissionChange}
onConfirmActionSuccess={handleAfterAction}
buttonDisabled={!pDiskParamsDefined || !isUserAllowedToMakeChanges}
popoverDisabled={isUserAllowedToMakeChanges}
/>
) : null}
</div>
);
};
Expand Down
36 changes: 18 additions & 18 deletions src/containers/VDiskPage/VDiskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
import {VDiskInfo} from '../../components/VDiskInfo/VDiskInfo';
import {api} from '../../store/reducers/api';
import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
import {vDiskApi} from '../../store/reducers/vdisk/vdisk';
import {valueIsDefined} from '../../utils';
Expand All @@ -33,6 +34,7 @@ export function VDiskPage() {
const dispatch = useTypedDispatch();

const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges);
const newDiskApiAvailable = useDiskPagesAvailable();

const [{nodeId, pDiskId, vDiskSlotId}] = useQueryParams({
nodeId: StringParam,
Expand Down Expand Up @@ -68,24 +70,22 @@ export function VDiskPage() {

const handleEvictVDisk = async (isRetry?: boolean) => {
if (vDiskIdParamsDefined) {
return window.api
.evictVDisk({
groupId: GroupID,
groupGeneration: GroupGeneration,
failRealmIdx: Ring,
failDomainIdx: Domain,
vDiskIdx: VDisk,
force: isRetry,
})
.then((response) => {
if (response?.result === false) {
const err = {
statusText: response.error,
retryPossible: response.forceRetryPossible,
};
throw err;
}
});
return window.api[newDiskApiAvailable ? 'evictVDisk' : 'evictVDiskOld']({
groupId: GroupID,
groupGeneration: GroupGeneration,
failRealmIdx: Ring,
failDomainIdx: Domain,
vDiskIdx: VDisk,
force: isRetry,
}).then((response) => {
if (response?.result === false) {
const err = {
statusText: response.error,
retryPossible: response.forceRetryPossible,
};
throw err;
}
});
}

return undefined;
Expand Down
60 changes: 60 additions & 0 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
DEV_ENABLE_TRACING_FOR_ALL_REQUESTS,
SECOND_IN_MS,
} from '../utils/constants';
import {createPDiskDeveloperUILink} from '../utils/developerUI/developerUI';
import {isAxiosError} from '../utils/response';
import type {Nullable} from '../utils/typecheckers';

Expand Down Expand Up @@ -629,6 +630,45 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
{concurrentId, requestConfig: {signal}},
);
}
evictVDiskOld({
groupId,
groupGeneration,
failRealmIdx,
failDomainIdx,
vDiskIdx,
}: {
groupId: string | number;
groupGeneration: string | number;
failRealmIdx: string | number;
failDomainIdx: string | number;
vDiskIdx: string | number;
}) {
// BSC Id is constant for all ydb clusters
const BSC_TABLET_ID = '72057594037932033';

return this.post(
this.getPath(`/tablets/app?TabletID=${BSC_TABLET_ID}&exec=1`),
{
Command: {
ReassignGroupDisk: {
GroupId: groupId,
GroupGeneration: groupGeneration,
FailRealmIdx: failRealmIdx,
FailDomainIdx: failDomainIdx,
VDiskIdx: vDiskIdx,
},
},
},
{},
{
headers: {
// This handler requires exactly this string
// Automatic headers may not suit
Accept: 'application/json',
},
},
);
}
evictVDisk({
groupId,
groupGeneration,
Expand Down Expand Up @@ -661,6 +701,26 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
},
);
}

restartPDiskOld({nodeId, pDiskId}: {nodeId: number | string; pDiskId: number | string}) {
const pDiskPath = createPDiskDeveloperUILink({
nodeId,
pDiskId,
host: this.getPath(''),
});

return this.post<ModifyDiskResponse>(
pDiskPath,
'restartPDisk=',
{},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
},
);
}

restartPDisk({
nodeId,
pDiskId,
Expand Down
15 changes: 13 additions & 2 deletions src/store/reducers/capabilities/capabilities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createSelector} from '@reduxjs/toolkit';

import type {Capability} from '../../../types/api/capabilities';
import type {RootState} from '../../defaultStore';
import type {AppDispatch, RootState} from '../../defaultStore';

import {api} from './../api';

Expand All @@ -23,10 +23,21 @@ export const capabilitiesApi = api.injectEndpoints({
overrideExisting: 'throw',
});

const selectCapabilities = capabilitiesApi.endpoints.getClusterCapabilities.select(undefined);
export const selectCapabilities =
capabilitiesApi.endpoints.getClusterCapabilities.select(undefined);

export const selectCapabilityVersion = createSelector(
(state: RootState) => state,
(_state: RootState, capability: Capability) => capability,
(state, capability) => selectCapabilities(state).data?.Capabilities?.[capability],
);

export async function queryCapability(
capability: Capability,
{dispatch, getState}: {dispatch: AppDispatch; getState: () => RootState},
) {
const thunk = capabilitiesApi.util.getRunningQueryThunk('getClusterCapabilities', undefined);
await dispatch(thunk);

return selectCapabilityVersion(getState(), capability) || 0;
}
34 changes: 32 additions & 2 deletions src/store/reducers/pdisk/pdisk.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type {TPDiskInfoResponse} from '../../../types/api/pdisk';
import {getPDiskId} from '../../../utils/disks/helpers';
import type {GetState} from '../../defaultStore';
import {api} from '../api';
import {queryCapability} from '../capabilities/capabilities';

import {preparePDiskDataResponse} from './utils';

Expand All @@ -11,10 +14,37 @@ interface PDiskParams {
export const pDiskApi = api.injectEndpoints({
endpoints: (build) => ({
getPdiskInfo: build.query({
queryFn: async ({nodeId, pDiskId}: PDiskParams, {signal}) => {
queryFn: async ({nodeId, pDiskId}: PDiskParams, {signal, getState, dispatch}) => {
const pDiskInfoHandlerVersion = await queryCapability('/pdisk/info', {
getState: getState as GetState,
dispatch,
});
const newApiAvailable = pDiskInfoHandlerVersion > 0;

let diskInfoPromise: Promise<TPDiskInfoResponse>;
if (newApiAvailable) {
diskInfoPromise = window.api.getPDiskInfo({nodeId, pDiskId}, {signal});
} else {
diskInfoPromise = window.api
.getNodeWhiteboardPDiskInfo({nodeId, pDiskId}, {signal})
.then((result) => {
if (result.PDiskStateInfo) {
return {
Whiteboard: {
PDisk: {
...result.PDiskStateInfo[0],
ExpectedSlotCount: undefined,
},
},
};
}
return {};
});
}

try {
const response = await Promise.all([
window.api.getPDiskInfo({nodeId, pDiskId}, {signal}),
diskInfoPromise,
window.api.getNodeInfo(nodeId, {signal}),
]);
const data = preparePDiskDataResponse(response);
Expand Down
Loading
Loading