diff --git a/src/components/PDiskInfo/PDiskInfo.tsx b/src/components/PDiskInfo/PDiskInfo.tsx index 995ea2b209..e6882b3838 100644 --- a/src/components/PDiskInfo/PDiskInfo.tsx +++ b/src/components/PDiskInfo/PDiskInfo.tsx @@ -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'; @@ -191,12 +190,11 @@ export function PDiskInfo({ className, }: PDiskInfoProps) { const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges); - const diskPagesAvailable = useDiskPagesAvailable(); const [generalInfo, statusInfo, spaceInfo, additionalInfo] = getPDiskInfo({ pDisk, nodeId, - withPDiskPageLink: withPDiskPageLink && diskPagesAvailable, + withPDiskPageLink, isUserAllowedToMakeChanges, }); diff --git a/src/components/VDiskInfo/VDiskInfo.tsx b/src/components/VDiskInfo/VDiskInfo.tsx index 35311a89c3..7c2bcaf815 100644 --- a/src/components/VDiskInfo/VDiskInfo.tsx +++ b/src/components/VDiskInfo/VDiskInfo.tsx @@ -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'; @@ -36,7 +35,6 @@ export function VDiskInfo({ withTitle, ...infoViewerProps }: VDiskInfoProps) { - const diskPagesAvailable = useDiskPagesAvailable(); const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges); const { @@ -154,7 +152,7 @@ export function VDiskInfo({ if (diskParamsDefined) { const links: React.ReactNode[] = []; - if (withVDiskPageLink && diskPagesAvailable) { + if (withVDiskPageLink) { const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId); links.push( { 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 { @@ -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(); @@ -131,6 +141,9 @@ export function Node(props: NodeProps) { ); } + case STRUCTURE: { + return ; + } case OVERVIEW: { return ; } @@ -140,7 +153,7 @@ export function Node(props: NodeProps) { } }; - if (loading) { + if (loading || !capabilitiesLoaded) { return ; } diff --git a/src/containers/Node/NodePages.ts b/src/containers/Node/NodePages.ts index 71c4998bc7..235b060092 100644 --- a/src/containers/Node/NodePages.ts +++ b/src/containers/Node/NodePages.ts @@ -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', diff --git a/src/containers/PDiskPage/PDiskPage.tsx b/src/containers/PDiskPage/PDiskPage.tsx index 150046556c..b2e4b7b86d 100644 --- a/src/containers/PDiskPage/PDiskPage.tsx +++ b/src/containers/PDiskPage/PDiskPage.tsx @@ -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'; @@ -61,6 +62,7 @@ export function PDiskPage() { const dispatch = useTypedDispatch(); const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges); + const newDiskApiAvailable = useDiskPagesAvailable(); const [{nodeId, pDiskId, activeTab}] = useQueryParams({ activeTab: StringParam, @@ -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 = { @@ -188,13 +192,15 @@ export function PDiskPage() { {pDiskPageKeyset('restart-pdisk-button')} - + {newDiskApiAvailable ? ( + + ) : null} ); }; diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx index 04e6ad41e3..5c87a038b2 100644 --- a/src/containers/VDiskPage/VDiskPage.tsx +++ b/src/containers/VDiskPage/VDiskPage.tsx @@ -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'; @@ -33,6 +34,7 @@ export function VDiskPage() { const dispatch = useTypedDispatch(); const isUserAllowedToMakeChanges = useTypedSelector(selectIsUserAllowedToMakeChanges); + const newDiskApiAvailable = useDiskPagesAvailable(); const [{nodeId, pDiskId, vDiskSlotId}] = useQueryParams({ nodeId: StringParam, @@ -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; diff --git a/src/services/api.ts b/src/services/api.ts index 6b18ec3044..a34ce54b39 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -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'; @@ -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, @@ -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( + pDiskPath, + 'restartPDisk=', + {}, + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + }, + ); + } + restartPDisk({ nodeId, pDiskId, diff --git a/src/store/reducers/capabilities/capabilities.ts b/src/store/reducers/capabilities/capabilities.ts index 47d4329ca0..759e583eca 100644 --- a/src/store/reducers/capabilities/capabilities.ts +++ b/src/store/reducers/capabilities/capabilities.ts @@ -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'; @@ -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; +} diff --git a/src/store/reducers/pdisk/pdisk.ts b/src/store/reducers/pdisk/pdisk.ts index 23722cdf65..b7fcbcf474 100644 --- a/src/store/reducers/pdisk/pdisk.ts +++ b/src/store/reducers/pdisk/pdisk.ts @@ -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'; @@ -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; + 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); diff --git a/src/store/reducers/pdisk/utils.ts b/src/store/reducers/pdisk/utils.ts index 76c68e516d..dadc74eca7 100644 --- a/src/store/reducers/pdisk/utils.ts +++ b/src/store/reducers/pdisk/utils.ts @@ -90,7 +90,7 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [ const diskSlots: PDiskData['SlotItems'] = [...vdisksSlots, ...emptySlots]; - if (logSlot) { + if (logSlot && diskSlots.length > 0) { diskSlots.unshift(logSlot); }