ydb-embedded-ui 4.5.1 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.js +1 -1
- package/dist/components/PoolUsage/PoolUsage.scss +1 -1
- package/dist/components/PoolUsage/PoolUsage.tsx +50 -0
- package/dist/containers/Node/Node.tsx +1 -1
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +19 -17
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +2 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +1 -1
- package/dist/containers/Tenant/Tenant.tsx +1 -1
- package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss +1 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -3
- package/dist/services/api.ts +2 -2
- package/dist/store/reducers/index.ts +3 -3
- package/dist/store/reducers/{network.js → network/network.ts} +11 -8
- package/dist/store/reducers/network/types.ts +16 -0
- package/dist/store/reducers/node/node.ts +102 -0
- package/dist/store/reducers/node/selectors.ts +59 -0
- package/dist/store/reducers/node/types.ts +44 -0
- package/dist/store/reducers/{schemaAcl.js → schemaAcl/schemaAcl.ts} +20 -9
- package/dist/store/reducers/schemaAcl/types.ts +15 -0
- package/dist/types/api/acl.ts +1 -1
- package/package.json +1 -1
- package/dist/components/PoolUsage/PoolUsage.js +0 -54
- package/dist/store/reducers/node.js +0 -141
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.5.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.1...v4.5.2) (2023-06-06)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* **Versions:** enable table dynamic render ([#416](https://github.com/ydb-platform/ydb-embedded-ui/issues/416)) ([3c877ea](https://github.com/ydb-platform/ydb-embedded-ui/commit/3c877ea88a0c4036213b38099676f473cf3ac2d6))
|
9
|
+
|
3
10
|
## [4.5.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.0...v4.5.1) (2023-06-02)
|
4
11
|
|
5
12
|
|
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
|
4
4
|
|
5
5
|
import InfoViewer from '../InfoViewer/InfoViewer';
|
6
6
|
import ProgressViewer from '../ProgressViewer/ProgressViewer';
|
7
|
-
import PoolUsage from '../PoolUsage/PoolUsage';
|
7
|
+
import {PoolUsage} from '../PoolUsage/PoolUsage';
|
8
8
|
|
9
9
|
import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
|
10
10
|
import {calcUptime} from '../../utils';
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import type {TPoolStats} from '../../types/api/nodes';
|
4
|
+
|
5
|
+
import './PoolUsage.scss';
|
6
|
+
|
7
|
+
const b = block('ydb-pool-usage');
|
8
|
+
|
9
|
+
const getLineType = (fillWidth: number) => {
|
10
|
+
let fillColor = 'green';
|
11
|
+
if (fillWidth > 60 && fillWidth <= 80) {
|
12
|
+
fillColor = 'yellow';
|
13
|
+
} else if (fillWidth > 80) {
|
14
|
+
fillColor = 'red';
|
15
|
+
}
|
16
|
+
|
17
|
+
return fillColor;
|
18
|
+
};
|
19
|
+
|
20
|
+
interface PoolUsageProps {
|
21
|
+
data?: TPoolStats;
|
22
|
+
}
|
23
|
+
|
24
|
+
export const PoolUsage = ({data: pool = {}}: PoolUsageProps) => {
|
25
|
+
const {Threads, Name = 'Unknown', Usage = 0} = pool;
|
26
|
+
const dataExist = Usage && Threads;
|
27
|
+
|
28
|
+
const value = Math.floor(Usage * 100);
|
29
|
+
const fillWidth = value > 100 ? 100 : value;
|
30
|
+
|
31
|
+
return (
|
32
|
+
<div className={b()}>
|
33
|
+
<div className={b('info')}>
|
34
|
+
<div className={b('pool-name')}>{Name}</div>
|
35
|
+
{dataExist && (
|
36
|
+
<div className={b('value')}>
|
37
|
+
<div className={b('percents')}>{value < 1 ? '<1' : value}%</div>
|
38
|
+
<div className={b('threads')}>(×{Threads})</div>
|
39
|
+
</div>
|
40
|
+
)}
|
41
|
+
</div>
|
42
|
+
<div className={b('visual')}>
|
43
|
+
<div
|
44
|
+
className={b('usage-line', {type: getLineType(fillWidth)})}
|
45
|
+
style={{width: `${fillWidth}%`}}
|
46
|
+
/>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
);
|
50
|
+
};
|
@@ -15,7 +15,7 @@ import NodeStructure from './NodeStructure/NodeStructure';
|
|
15
15
|
import {Loader} from '../../components/Loader';
|
16
16
|
import {BasicNodeViewer} from '../../components/BasicNodeViewer';
|
17
17
|
|
18
|
-
import {getNodeInfo, resetNode} from '../../store/reducers/node';
|
18
|
+
import {getNodeInfo, resetNode} from '../../store/reducers/node/node';
|
19
19
|
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
|
20
20
|
import {setHeader} from '../../store/reducers/header';
|
21
21
|
import {AutoFetcher} from '../../utils/autofetcher';
|
@@ -1,16 +1,19 @@
|
|
1
1
|
import {useEffect, useRef, useMemo} from 'react';
|
2
|
-
import {useDispatch
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
3
|
import url from 'url';
|
4
4
|
import _ from 'lodash';
|
5
5
|
|
6
6
|
import cn from 'bem-cn-lite';
|
7
7
|
|
8
|
-
import {PDisk} from './Pdisk';
|
9
8
|
import {Loader} from '../.././../components/Loader';
|
10
9
|
|
11
|
-
import {getNodeStructure
|
10
|
+
import {getNodeStructure} from '../../../store/reducers/node/node';
|
11
|
+
import {selectNodeStructure} from '../../../store/reducers/node/selectors';
|
12
12
|
|
13
13
|
import {AutoFetcher} from '../../../utils/autofetcher';
|
14
|
+
import {useTypedSelector} from '../../../utils/hooks';
|
15
|
+
|
16
|
+
import {PDisk} from './Pdisk';
|
14
17
|
|
15
18
|
import './NodeStructure.scss';
|
16
19
|
|
@@ -32,20 +35,19 @@ interface NodeStructureProps {
|
|
32
35
|
|
33
36
|
const autofetcher = new AutoFetcher();
|
34
37
|
|
35
|
-
function NodeStructure(
|
38
|
+
function NodeStructure({nodeId, className, additionalNodesInfo}: NodeStructureProps) {
|
36
39
|
const dispatch = useDispatch();
|
37
40
|
|
38
|
-
const nodeStructure
|
41
|
+
const nodeStructure = useTypedSelector(selectNodeStructure);
|
39
42
|
|
40
|
-
const loadingStructure =
|
41
|
-
const
|
42
|
-
const nodeData = useSelector((state: any) => state.node?.data?.SystemStateInfo?.[0]);
|
43
|
+
const {loadingStructure, wasLoadedStructure} = useTypedSelector((state) => state.node);
|
44
|
+
const nodeData = useTypedSelector((state) => state.node?.data?.SystemStateInfo?.[0]);
|
43
45
|
|
44
46
|
const nodeHref = useMemo(() => {
|
45
|
-
return
|
46
|
-
?
|
47
|
+
return additionalNodesInfo?.getNodeRef
|
48
|
+
? additionalNodesInfo.getNodeRef(nodeData)
|
47
49
|
: undefined;
|
48
|
-
}, [nodeData,
|
50
|
+
}, [nodeData, additionalNodesInfo]);
|
49
51
|
|
50
52
|
const {pdiskId: pdiskIdFromUrl, vdiskId: vdiskIdFromUrl} = url.parse(
|
51
53
|
window.location.href,
|
@@ -71,16 +73,16 @@ function NodeStructure(props: NodeStructureProps) {
|
|
71
73
|
}, []);
|
72
74
|
|
73
75
|
useEffect(() => {
|
74
|
-
dispatch(getNodeStructure(
|
76
|
+
dispatch(getNodeStructure(nodeId));
|
75
77
|
autofetcher.start();
|
76
|
-
autofetcher.fetch(() => dispatch(getNodeStructure(
|
78
|
+
autofetcher.fetch(() => dispatch(getNodeStructure(nodeId)));
|
77
79
|
|
78
80
|
return () => {
|
79
81
|
scrolled.current = false;
|
80
82
|
isReady.current = false;
|
81
83
|
autofetcher.stop();
|
82
84
|
};
|
83
|
-
}, [
|
85
|
+
}, [nodeId, dispatch]);
|
84
86
|
|
85
87
|
useEffect(() => {
|
86
88
|
if (!_.isEmpty(nodeStructure) && scrollContainer) {
|
@@ -98,9 +100,9 @@ function NodeStructure(props: NodeStructureProps) {
|
|
98
100
|
|
99
101
|
if (vdiskIdFromUrl) {
|
100
102
|
const vDisks = nodeStructure[pdiskIdFromUrl as string]?.vDisks;
|
101
|
-
const vDisk = vDisks?.find((el
|
103
|
+
const vDisk = vDisks?.find((el) => el.id === vdiskIdFromUrl);
|
102
104
|
const dataTable = vDisk ? document.querySelector('.data-table') : undefined;
|
103
|
-
const order = vDisk?.order;
|
105
|
+
const order = vDisk?.order || 0;
|
104
106
|
|
105
107
|
if (dataTable) {
|
106
108
|
scrollToVdisk += (dataTable as HTMLElement).offsetTop + 40 * order;
|
@@ -147,7 +149,7 @@ function NodeStructure(props: NodeStructureProps) {
|
|
147
149
|
|
148
150
|
return (
|
149
151
|
<div className={b()} ref={scrollContainerRef}>
|
150
|
-
<div className={
|
152
|
+
<div className={className}>{renderContent()}</div>
|
151
153
|
</div>
|
152
154
|
);
|
153
155
|
}
|
@@ -12,7 +12,7 @@ import {Icon} from '../../../../components/Icon';
|
|
12
12
|
import {ProblemFilter} from '../../../../components/ProblemFilter';
|
13
13
|
import {Illustration} from '../../../../components/Illustration';
|
14
14
|
|
15
|
-
import {getNetworkInfo, setDataWasNotLoaded} from '../../../../store/reducers/network';
|
15
|
+
import {getNetworkInfo, setDataWasNotLoaded} from '../../../../store/reducers/network/network';
|
16
16
|
import {hideTooltip, showTooltip} from '../../../../store/reducers/tooltip';
|
17
17
|
import {changeFilter, ProblemFilterValues} from '../../../../store/reducers/settings/settings';
|
18
18
|
import {AutoFetcher} from '../../../../utils/autofetcher';
|
@@ -361,7 +361,7 @@ class Network extends React.Component {
|
|
361
361
|
}
|
362
362
|
|
363
363
|
const mapStateToProps = (state) => {
|
364
|
-
const {wasLoaded, loading, data: netWorkInfo} = state.network;
|
364
|
+
const {wasLoaded, loading, data: netWorkInfo = {}} = state.network;
|
365
365
|
const {autorefresh} = state.schema;
|
366
366
|
return {
|
367
367
|
netWorkInfo,
|
@@ -6,7 +6,7 @@ import {Loader} from '@gravity-ui/uikit';
|
|
6
6
|
|
7
7
|
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
|
8
8
|
import InfoViewer from '../../../../components/InfoViewer/InfoViewer';
|
9
|
-
import PoolUsage from '../../../../components/PoolUsage/PoolUsage';
|
9
|
+
import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
|
10
10
|
import {Tablet} from '../../../../components/Tablet';
|
11
11
|
|
12
12
|
import {getTenantInfo} from '../../../../store/reducers/tenant/tenant';
|
@@ -11,7 +11,7 @@ import {useTypedSelector} from '../../utils/hooks';
|
|
11
11
|
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
|
12
12
|
import {setHeader} from '../../store/reducers/header';
|
13
13
|
import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
|
14
|
-
import {getSchemaAcl} from '../../store/reducers/schemaAcl';
|
14
|
+
import {getSchemaAcl} from '../../store/reducers/schemaAcl/schemaAcl';
|
15
15
|
import {getTenantInfo, clearTenant} from '../../store/reducers/tenant/tenant';
|
16
16
|
|
17
17
|
import SplitPane from '../../components/SplitPane';
|
@@ -2,6 +2,7 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
|
|
2
2
|
|
3
3
|
import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
|
4
4
|
import {isUnavailableNode} from '../../../utils/nodes';
|
5
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
|
5
6
|
import {formatBytes} from '../../../utils';
|
6
7
|
import {getDefaultNodePath} from '../../Node/NodePages';
|
7
8
|
|
@@ -112,9 +113,7 @@ export const NodesTable = ({nodes}: NodesTableProps) => {
|
|
112
113
|
theme="yandex-cloud"
|
113
114
|
data={nodes}
|
114
115
|
columns={columns}
|
115
|
-
settings={
|
116
|
-
displayIndices: false,
|
117
|
-
}}
|
116
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
118
117
|
/>
|
119
118
|
);
|
120
119
|
};
|
package/dist/services/api.ts
CHANGED
@@ -5,13 +5,13 @@ import cluster from './cluster/cluster';
|
|
5
5
|
import clusterNodes from './clusterNodes/clusterNodes';
|
6
6
|
import tenant from './tenant/tenant';
|
7
7
|
import storage from './storage';
|
8
|
-
import node from './node';
|
8
|
+
import node from './node/node';
|
9
9
|
import tooltip from './tooltip';
|
10
10
|
import tablets from './tablets';
|
11
11
|
import heatmap from './heatmap';
|
12
12
|
import schema from './schema';
|
13
13
|
import host from './host';
|
14
|
-
import network from './network';
|
14
|
+
import network from './network/network';
|
15
15
|
import tenants from './tenants/tenants';
|
16
16
|
import tablet from './tablet';
|
17
17
|
import topic from './topic';
|
@@ -23,7 +23,7 @@ import settings from './settings/settings';
|
|
23
23
|
import preview from './preview';
|
24
24
|
import nodesList from './nodesList';
|
25
25
|
import describe from './describe';
|
26
|
-
import schemaAcl from './schemaAcl';
|
26
|
+
import schemaAcl from './schemaAcl/schemaAcl';
|
27
27
|
import executeTopQueries from './executeTopQueries';
|
28
28
|
import healthcheckInfo from './healthcheckInfo';
|
29
29
|
import shardsWorkload from './shardsWorkload';
|
@@ -1,20 +1,23 @@
|
|
1
|
-
import {
|
2
|
-
import '../../services/api';
|
1
|
+
import {Reducer} from 'redux';
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
import '../../../services/api';
|
4
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
5
|
+
|
6
|
+
import type {NetworkAction, NetworkState} from './types';
|
7
|
+
|
8
|
+
export const FETCH_ALL_NODES_NETWORK = createRequestActionTypes(
|
9
|
+
'network',
|
6
10
|
'FETCH_ALL_NODES_NETWORK',
|
7
11
|
);
|
8
12
|
|
9
13
|
const SET_DATA_WAS_NOT_LOADED = 'network/SET_DATA_WAS_NOT_LOADED';
|
10
14
|
|
11
15
|
const initialState = {
|
12
|
-
data: {},
|
13
16
|
loading: false,
|
14
17
|
wasLoaded: false,
|
15
18
|
};
|
16
19
|
|
17
|
-
const network = (state = initialState, action) => {
|
20
|
+
const network: Reducer<NetworkState, NetworkAction> = (state = initialState, action) => {
|
18
21
|
switch (action.type) {
|
19
22
|
case FETCH_ALL_NODES_NETWORK.REQUEST: {
|
20
23
|
return {
|
@@ -53,10 +56,10 @@ const network = (state = initialState, action) => {
|
|
53
56
|
export const setDataWasNotLoaded = () => {
|
54
57
|
return {
|
55
58
|
type: SET_DATA_WAS_NOT_LOADED,
|
56
|
-
};
|
59
|
+
} as const;
|
57
60
|
};
|
58
61
|
|
59
|
-
export const getNetworkInfo = (tenant) => {
|
62
|
+
export const getNetworkInfo = (tenant: string) => {
|
60
63
|
return createApiRequest({
|
61
64
|
request: window.api.getNetwork(tenant),
|
62
65
|
actions: FETCH_ALL_NODES_NETWORK,
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import type {IResponseError} from '../../../types/api/error';
|
2
|
+
import type {TNetInfo} from '../../../types/api/netInfo';
|
3
|
+
import type {ApiRequestAction} from '../../utils';
|
4
|
+
|
5
|
+
import {FETCH_ALL_NODES_NETWORK, setDataWasNotLoaded} from './network';
|
6
|
+
|
7
|
+
export interface NetworkState {
|
8
|
+
loading: boolean;
|
9
|
+
wasLoaded: boolean;
|
10
|
+
data?: TNetInfo;
|
11
|
+
error?: IResponseError;
|
12
|
+
}
|
13
|
+
|
14
|
+
export type NetworkAction =
|
15
|
+
| ApiRequestAction<typeof FETCH_ALL_NODES_NETWORK, TNetInfo, IResponseError>
|
16
|
+
| ReturnType<typeof setDataWasNotLoaded>;
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import {Reducer} from 'redux';
|
2
|
+
|
3
|
+
import '../../../services/api';
|
4
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
5
|
+
|
6
|
+
import type {NodeAction, NodeState} from './types';
|
7
|
+
|
8
|
+
export const FETCH_NODE = createRequestActionTypes('node', 'FETCH_NODE');
|
9
|
+
export const FETCH_NODE_STRUCTURE = createRequestActionTypes('node', 'FETCH_NODE_STRUCTURE');
|
10
|
+
|
11
|
+
const RESET_NODE = 'node/RESET_NODE';
|
12
|
+
|
13
|
+
const initialState = {
|
14
|
+
data: {},
|
15
|
+
loading: true,
|
16
|
+
wasLoaded: false,
|
17
|
+
nodeStructure: {},
|
18
|
+
loadingStructure: true,
|
19
|
+
wasLoadedStructure: false,
|
20
|
+
};
|
21
|
+
|
22
|
+
const node: Reducer<NodeState, NodeAction> = (state = initialState, action) => {
|
23
|
+
switch (action.type) {
|
24
|
+
case FETCH_NODE.REQUEST: {
|
25
|
+
return {
|
26
|
+
...state,
|
27
|
+
loading: true,
|
28
|
+
};
|
29
|
+
}
|
30
|
+
case FETCH_NODE.SUCCESS: {
|
31
|
+
return {
|
32
|
+
...state,
|
33
|
+
data: action.data,
|
34
|
+
loading: false,
|
35
|
+
wasLoaded: true,
|
36
|
+
error: undefined,
|
37
|
+
};
|
38
|
+
}
|
39
|
+
case FETCH_NODE.FAILURE: {
|
40
|
+
return {
|
41
|
+
...state,
|
42
|
+
error: action.error,
|
43
|
+
loading: false,
|
44
|
+
};
|
45
|
+
}
|
46
|
+
case FETCH_NODE_STRUCTURE.REQUEST: {
|
47
|
+
return {
|
48
|
+
...state,
|
49
|
+
loadingStructure: true,
|
50
|
+
};
|
51
|
+
}
|
52
|
+
case FETCH_NODE_STRUCTURE.SUCCESS: {
|
53
|
+
return {
|
54
|
+
...state,
|
55
|
+
nodeStructure: action.data,
|
56
|
+
loadingStructure: false,
|
57
|
+
wasLoadedStructure: true,
|
58
|
+
errorStructure: undefined,
|
59
|
+
};
|
60
|
+
}
|
61
|
+
case FETCH_NODE_STRUCTURE.FAILURE: {
|
62
|
+
return {
|
63
|
+
...state,
|
64
|
+
errorStructure: action.error,
|
65
|
+
loadingStructure: false,
|
66
|
+
};
|
67
|
+
}
|
68
|
+
case RESET_NODE: {
|
69
|
+
return {
|
70
|
+
...state,
|
71
|
+
data: {},
|
72
|
+
wasLoaded: false,
|
73
|
+
nodeStructure: {},
|
74
|
+
wasLoadedStructure: false,
|
75
|
+
};
|
76
|
+
}
|
77
|
+
default:
|
78
|
+
return state;
|
79
|
+
}
|
80
|
+
};
|
81
|
+
|
82
|
+
export const getNodeInfo = (id: string) => {
|
83
|
+
return createApiRequest({
|
84
|
+
request: window.api.getNodeInfo(id),
|
85
|
+
actions: FETCH_NODE,
|
86
|
+
});
|
87
|
+
};
|
88
|
+
|
89
|
+
export const getNodeStructure = (nodeId: string) => {
|
90
|
+
return createApiRequest({
|
91
|
+
request: window.api.getStorageInfo({nodeId}, {concurrentId: 'getNodeStructure'}),
|
92
|
+
actions: FETCH_NODE_STRUCTURE,
|
93
|
+
});
|
94
|
+
};
|
95
|
+
|
96
|
+
export function resetNode() {
|
97
|
+
return {
|
98
|
+
type: RESET_NODE,
|
99
|
+
} as const;
|
100
|
+
}
|
101
|
+
|
102
|
+
export default node;
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import type {Selector} from 'reselect';
|
2
|
+
import {createSelector} from 'reselect';
|
3
|
+
|
4
|
+
import {stringifyVdiskId} from '../../../utils';
|
5
|
+
|
6
|
+
import type {
|
7
|
+
NodeStateSlice,
|
8
|
+
PreparedNodeStructure,
|
9
|
+
PreparedStructureVDisk,
|
10
|
+
RawNodeStructure,
|
11
|
+
} from './types';
|
12
|
+
|
13
|
+
const selectNodeId = (state: NodeStateSlice) => state.node?.data?.SystemStateInfo?.[0].NodeId;
|
14
|
+
|
15
|
+
const selectRawNodeStructure = (state: NodeStateSlice) => state.node?.nodeStructure;
|
16
|
+
|
17
|
+
export const selectNodeStructure: Selector<NodeStateSlice, PreparedNodeStructure> = createSelector(
|
18
|
+
[selectNodeId, selectRawNodeStructure],
|
19
|
+
(nodeId, storageInfo) => {
|
20
|
+
const pools = storageInfo?.StoragePools;
|
21
|
+
const structure: RawNodeStructure = {};
|
22
|
+
|
23
|
+
pools?.forEach((pool) => {
|
24
|
+
const groups = pool.Groups;
|
25
|
+
groups?.forEach((group) => {
|
26
|
+
const vDisks = group.VDisks?.filter((el) => el.NodeId === nodeId);
|
27
|
+
vDisks?.forEach((vd) => {
|
28
|
+
const vDiskId = stringifyVdiskId(vd.VDiskId);
|
29
|
+
const pDiskId = vd.PDisk?.PDiskId;
|
30
|
+
if (!structure[String(pDiskId)]) {
|
31
|
+
structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk};
|
32
|
+
}
|
33
|
+
structure[String(pDiskId)].vDisks[vDiskId] = {
|
34
|
+
...vd,
|
35
|
+
// VDisk doesn't have its own StoragePoolName when located inside StoragePool data
|
36
|
+
StoragePoolName: pool.Name,
|
37
|
+
};
|
38
|
+
});
|
39
|
+
});
|
40
|
+
});
|
41
|
+
|
42
|
+
const structureWithVdisksArray = Object.keys(structure).reduce<PreparedNodeStructure>(
|
43
|
+
(preparedStructure, el) => {
|
44
|
+
const vDisks = structure[el].vDisks;
|
45
|
+
const vDisksArray = Object.keys(vDisks).reduce<PreparedStructureVDisk[]>(
|
46
|
+
(acc, key, index) => {
|
47
|
+
acc.push({...vDisks[key], id: key, order: index});
|
48
|
+
return acc;
|
49
|
+
},
|
50
|
+
[],
|
51
|
+
);
|
52
|
+
preparedStructure[el] = {...structure[el], vDisks: vDisksArray};
|
53
|
+
return preparedStructure;
|
54
|
+
},
|
55
|
+
{},
|
56
|
+
);
|
57
|
+
return structureWithVdisksArray;
|
58
|
+
},
|
59
|
+
);
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import type {IResponseError} from '../../../types/api/error';
|
2
|
+
import type {TPDiskStateInfo} from '../../../types/api/pdisk';
|
3
|
+
import type {TStorageInfo} from '../../../types/api/storage';
|
4
|
+
import type {TEvSystemStateResponse} from '../../../types/api/systemState';
|
5
|
+
import type {TVDiskStateInfo} from '../../../types/api/vdisk';
|
6
|
+
import type {ApiRequestAction} from '../../utils';
|
7
|
+
|
8
|
+
import {FETCH_NODE, FETCH_NODE_STRUCTURE, resetNode} from './node';
|
9
|
+
|
10
|
+
interface RawStructurePDisk extends TPDiskStateInfo {
|
11
|
+
vDisks: Record<string, TVDiskStateInfo>;
|
12
|
+
}
|
13
|
+
|
14
|
+
export type RawNodeStructure = Record<string, RawStructurePDisk>;
|
15
|
+
|
16
|
+
export interface PreparedStructureVDisk extends TVDiskStateInfo {
|
17
|
+
id: string;
|
18
|
+
order: number;
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface PreparedStructurePDisk extends TPDiskStateInfo {
|
22
|
+
vDisks: PreparedStructureVDisk[];
|
23
|
+
}
|
24
|
+
|
25
|
+
export type PreparedNodeStructure = Record<string, PreparedStructurePDisk>;
|
26
|
+
|
27
|
+
export interface NodeState {
|
28
|
+
data: TEvSystemStateResponse;
|
29
|
+
loading: boolean;
|
30
|
+
wasLoaded: boolean;
|
31
|
+
|
32
|
+
nodeStructure: TStorageInfo;
|
33
|
+
loadingStructure: boolean;
|
34
|
+
wasLoadedStructure: boolean;
|
35
|
+
}
|
36
|
+
|
37
|
+
export type NodeAction =
|
38
|
+
| ApiRequestAction<typeof FETCH_NODE, TEvSystemStateResponse, IResponseError>
|
39
|
+
| ApiRequestAction<typeof FETCH_NODE_STRUCTURE, TStorageInfo, IResponseError>
|
40
|
+
| ReturnType<typeof resetNode>;
|
41
|
+
|
42
|
+
export interface NodeStateSlice {
|
43
|
+
node: NodeState;
|
44
|
+
}
|
@@ -1,10 +1,18 @@
|
|
1
|
-
import {
|
2
|
-
import '../../services/api';
|
3
|
-
import _ from 'lodash';
|
1
|
+
import type {Reducer} from 'redux';
|
4
2
|
|
5
|
-
|
3
|
+
import '../../../services/api';
|
4
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
6
5
|
|
7
|
-
|
6
|
+
import type {SchemaAclAction, SchemaAclState} from './types';
|
7
|
+
|
8
|
+
export const FETCH_SCHEMA_ACL = createRequestActionTypes('schemaAcl', 'FETCH_SCHEMA_ACL');
|
9
|
+
|
10
|
+
const initialState = {
|
11
|
+
loading: false,
|
12
|
+
wasLoaded: false,
|
13
|
+
};
|
14
|
+
|
15
|
+
const schemaAcl: Reducer<SchemaAclState, SchemaAclAction> = (state = initialState, action) => {
|
8
16
|
switch (action.type) {
|
9
17
|
case FETCH_SCHEMA_ACL.REQUEST: {
|
10
18
|
return {
|
@@ -13,13 +21,16 @@ const schemaAcl = function z(state = {loading: false, wasLoaded: false, acl: und
|
|
13
21
|
};
|
14
22
|
}
|
15
23
|
case FETCH_SCHEMA_ACL.SUCCESS: {
|
24
|
+
const acl = action.data.Common?.ACL;
|
25
|
+
const owner = action.data.Common?.Owner;
|
26
|
+
|
16
27
|
return {
|
17
28
|
...state,
|
18
|
-
|
19
|
-
|
20
|
-
owner: _.get(action.data, 'Common.Owner'),
|
29
|
+
acl,
|
30
|
+
owner,
|
21
31
|
loading: false,
|
22
32
|
wasLoaded: true,
|
33
|
+
error: undefined,
|
23
34
|
};
|
24
35
|
}
|
25
36
|
case FETCH_SCHEMA_ACL.FAILURE: {
|
@@ -34,7 +45,7 @@ const schemaAcl = function z(state = {loading: false, wasLoaded: false, acl: und
|
|
34
45
|
}
|
35
46
|
};
|
36
47
|
|
37
|
-
export function getSchemaAcl({path}) {
|
48
|
+
export function getSchemaAcl({path}: {path: string}) {
|
38
49
|
return createApiRequest({
|
39
50
|
request: window.api.getSchemaAcl({path}),
|
40
51
|
actions: FETCH_SCHEMA_ACL,
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import type {TACE, TMetaInfo} from '../../../types/api/acl';
|
2
|
+
import type {IResponseError} from '../../../types/api/error';
|
3
|
+
import type {ApiRequestAction} from '../../utils';
|
4
|
+
|
5
|
+
import {FETCH_SCHEMA_ACL} from './schemaAcl';
|
6
|
+
|
7
|
+
export interface SchemaAclState {
|
8
|
+
loading: boolean
|
9
|
+
wasLoaded: boolean
|
10
|
+
acl?: TACE[]
|
11
|
+
owner?: string
|
12
|
+
error?: IResponseError
|
13
|
+
}
|
14
|
+
|
15
|
+
export type SchemaAclAction = ApiRequestAction<typeof FETCH_SCHEMA_ACL, TMetaInfo, IResponseError>;
|
package/dist/types/api/acl.ts
CHANGED
package/package.json
CHANGED
@@ -1,54 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import cn from 'bem-cn-lite';
|
3
|
-
import './PoolUsage.scss';
|
4
|
-
import PropTypes from 'prop-types';
|
5
|
-
const b = cn('pool-usage');
|
6
|
-
|
7
|
-
const formatUsage = (usage) => (typeof usage === 'undefined' ? '' : Math.floor(usage * 100));
|
8
|
-
const getLineType = (fillWidth) => {
|
9
|
-
let fillColor = 'green';
|
10
|
-
if (fillWidth > 60 && fillWidth <= 80) {
|
11
|
-
fillColor = 'yellow';
|
12
|
-
} else if (fillWidth > 80) {
|
13
|
-
fillColor = 'red';
|
14
|
-
}
|
15
|
-
|
16
|
-
return fillColor;
|
17
|
-
};
|
18
|
-
export class PoolUsage extends React.Component {
|
19
|
-
static propTypes = {
|
20
|
-
data: PropTypes.object.isRequired,
|
21
|
-
};
|
22
|
-
|
23
|
-
render() {
|
24
|
-
const {data: pool = {}} = this.props;
|
25
|
-
|
26
|
-
const {Threads, Name = 'Unknown', Usage} = pool;
|
27
|
-
const dataExist = Usage && Threads;
|
28
|
-
|
29
|
-
const value = formatUsage(pool.Usage);
|
30
|
-
const fillWidth = value > 100 ? 100 : value;
|
31
|
-
|
32
|
-
return (
|
33
|
-
<div className={b()}>
|
34
|
-
<div className={b('info')}>
|
35
|
-
<div className={b('pool-name')}>{Name}</div>
|
36
|
-
{dataExist && (
|
37
|
-
<div className={b('value')}>
|
38
|
-
<div className={b('percents')}>{value < 1 ? '<1' : value}%</div>
|
39
|
-
<div className={b('threads')}>(×{Threads})</div>
|
40
|
-
</div>
|
41
|
-
)}
|
42
|
-
</div>
|
43
|
-
<div className={b('visual')}>
|
44
|
-
<div
|
45
|
-
className={b('usage-line', {type: getLineType(fillWidth)})}
|
46
|
-
style={{width: `${fillWidth}%`}}
|
47
|
-
/>
|
48
|
-
</div>
|
49
|
-
</div>
|
50
|
-
);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
export default PoolUsage;
|
@@ -1,141 +0,0 @@
|
|
1
|
-
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
|
-
import '../../services/api';
|
3
|
-
import {stringifyVdiskId} from '../../utils';
|
4
|
-
import {createSelector} from 'reselect';
|
5
|
-
|
6
|
-
const FETCH_NODE = createRequestActionTypes('node', 'FETCH_NODE');
|
7
|
-
const FETCH_NODE_STRUCTURE = createRequestActionTypes('node', 'FETCH_NODE_STRUCTURE');
|
8
|
-
const RESET_NODE = 'node/RESET_NODE';
|
9
|
-
|
10
|
-
const node = (
|
11
|
-
state = {
|
12
|
-
data: {},
|
13
|
-
loading: true,
|
14
|
-
wasLoaded: false,
|
15
|
-
nodeStructure: {},
|
16
|
-
loadingStructure: true,
|
17
|
-
wasLoadedStructure: false,
|
18
|
-
},
|
19
|
-
action,
|
20
|
-
) => {
|
21
|
-
switch (action.type) {
|
22
|
-
case FETCH_NODE.REQUEST: {
|
23
|
-
return {
|
24
|
-
...state,
|
25
|
-
loading: true,
|
26
|
-
};
|
27
|
-
}
|
28
|
-
case FETCH_NODE.SUCCESS: {
|
29
|
-
return {
|
30
|
-
...state,
|
31
|
-
data: action.data,
|
32
|
-
loading: false,
|
33
|
-
wasLoaded: true,
|
34
|
-
error: undefined,
|
35
|
-
};
|
36
|
-
}
|
37
|
-
case FETCH_NODE.FAILURE: {
|
38
|
-
return {
|
39
|
-
...state,
|
40
|
-
error: action.error,
|
41
|
-
loading: false,
|
42
|
-
};
|
43
|
-
}
|
44
|
-
case FETCH_NODE_STRUCTURE.REQUEST: {
|
45
|
-
return {
|
46
|
-
...state,
|
47
|
-
loadingStructure: true,
|
48
|
-
};
|
49
|
-
}
|
50
|
-
case FETCH_NODE_STRUCTURE.SUCCESS: {
|
51
|
-
return {
|
52
|
-
...state,
|
53
|
-
nodeStructure: action.data,
|
54
|
-
loadingStructure: false,
|
55
|
-
wasLoadedStructure: true,
|
56
|
-
errorStructure: undefined,
|
57
|
-
};
|
58
|
-
}
|
59
|
-
case FETCH_NODE_STRUCTURE.FAILURE: {
|
60
|
-
return {
|
61
|
-
...state,
|
62
|
-
errorStructure: action.error,
|
63
|
-
loadingStructure: false,
|
64
|
-
};
|
65
|
-
}
|
66
|
-
case RESET_NODE: {
|
67
|
-
return {
|
68
|
-
...state,
|
69
|
-
data: {},
|
70
|
-
wasLoaded: false,
|
71
|
-
nodeStructure: {},
|
72
|
-
wasLoadedStructure: false,
|
73
|
-
};
|
74
|
-
}
|
75
|
-
default:
|
76
|
-
return state;
|
77
|
-
}
|
78
|
-
};
|
79
|
-
|
80
|
-
export const getNodeInfo = (id) => {
|
81
|
-
return createApiRequest({
|
82
|
-
request: window.api.getNodeInfo(id),
|
83
|
-
actions: FETCH_NODE,
|
84
|
-
});
|
85
|
-
};
|
86
|
-
|
87
|
-
export const getNodeStructure = (nodeId) => {
|
88
|
-
return createApiRequest({
|
89
|
-
request: window.api.getStorageInfo({nodeId}, {concurrentId: 'getNodeStructure'}),
|
90
|
-
actions: FETCH_NODE_STRUCTURE,
|
91
|
-
});
|
92
|
-
};
|
93
|
-
|
94
|
-
export function resetNode() {
|
95
|
-
return {
|
96
|
-
type: RESET_NODE,
|
97
|
-
};
|
98
|
-
}
|
99
|
-
|
100
|
-
const getNodeId = (state) => state.node?.data?.SystemStateInfo?.[0].NodeId;
|
101
|
-
|
102
|
-
const getRawNodeStructure = (state) => state.node?.nodeStructure;
|
103
|
-
|
104
|
-
export const selectNodeStructure = createSelector(
|
105
|
-
[getNodeId, getRawNodeStructure],
|
106
|
-
(nodeId, rawNodeStructure) => {
|
107
|
-
const pools = rawNodeStructure?.StoragePools;
|
108
|
-
const structure = {};
|
109
|
-
pools?.forEach((pool) => {
|
110
|
-
const groups = pool.Groups;
|
111
|
-
groups?.forEach((group) => {
|
112
|
-
const vDisks = group.VDisks?.filter((el) => el.NodeId === nodeId);
|
113
|
-
vDisks?.forEach((vd) => {
|
114
|
-
const vDiskId = stringifyVdiskId(vd.VDiskId);
|
115
|
-
const pDiskId = vd.PDisk?.PDiskId;
|
116
|
-
if (!structure[String(pDiskId)]) {
|
117
|
-
structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk};
|
118
|
-
}
|
119
|
-
structure[String(pDiskId)].vDisks[vDiskId] = {
|
120
|
-
...vd,
|
121
|
-
// VDisk doesn't have its own StoragePoolName when located inside StoragePool data
|
122
|
-
StoragePoolName: pool.Name,
|
123
|
-
};
|
124
|
-
});
|
125
|
-
});
|
126
|
-
});
|
127
|
-
|
128
|
-
const structureWithVdisksArray = Object.keys(structure).reduce((acc, el) => {
|
129
|
-
const vDisks = structure[el].vDisks;
|
130
|
-
const vDisksArray = Object.keys(vDisks).reduce((acc, key, index) => {
|
131
|
-
acc.push({...vDisks[key], id: key, order: index});
|
132
|
-
return acc;
|
133
|
-
}, []);
|
134
|
-
acc[el] = {...structure[el], vDisks: vDisksArray};
|
135
|
-
return acc;
|
136
|
-
}, {});
|
137
|
-
return structureWithVdisksArray;
|
138
|
-
},
|
139
|
-
);
|
140
|
-
|
141
|
-
export default node;
|