feat: add grafana link

This commit is contained in:
jialin
2026-02-04 20:14:25 +08:00
parent 7fd2b43704
commit 8690de00a9
13 changed files with 87 additions and 14 deletions
+2 -1
View File
@@ -64,7 +64,8 @@ const icons = {
StarOutlined: React.createElement(StarOutlined), StarOutlined: React.createElement(StarOutlined),
Charger: React.createElement(IconFont, { type: 'icon-charger' }), Charger: React.createElement(IconFont, { type: 'icon-charger' }),
Disabled: React.createElement(IconFont, { type: 'icon-disabled' }), Disabled: React.createElement(IconFont, { type: 'icon-disabled' }),
CopyOutlined: React.createElement(CopyOutlined) CopyOutlined: React.createElement(CopyOutlined),
Metrics: React.createElement(IconFont, { type: 'icon-metrics' })
}; };
export default icons; export default icons;
+1 -1
View File
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
// import './iconfont/iconfont.js'; // import './iconfont/iconfont.js';
const IconFont = createFromIconfontCN({ const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4613488_lqs6kytf9t8.js' scriptUrl: '//at.alicdn.com/t/c/font_4613488_n2otiiv2h4s.js'
}); });
export default IconFont; export default IconFont;
+2 -1
View File
@@ -106,5 +106,6 @@ export default {
'resources.worker.maintenance.tips': 'resources.worker.maintenance.tips':
'When enter the maintenance mode, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'When enter the maintenance mode, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
'resources.worker.noCluster.tips': 'resources.worker.noCluster.tips':
'No available clusters. Please create a cluster before adding a node.' 'No available clusters. Please create a cluster before adding a node.',
'resources.metrics.details': 'Metrics Details'
}; };
+2 -1
View File
@@ -107,7 +107,8 @@ export default {
'resources.worker.maintenance.tips': 'resources.worker.maintenance.tips':
'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
'resources.worker.noCluster.tips': 'resources.worker.noCluster.tips':
'No available clusters. Please create a cluster before adding a node.' 'No available clusters. Please create a cluster before adding a node.',
'resources.metrics.details': 'Metrics Details'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
+4 -2
View File
@@ -105,7 +105,8 @@ export default {
'resources.worker.maintenance.tips': 'resources.worker.maintenance.tips':
'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
'resources.worker.noCluster.tips': 'resources.worker.noCluster.tips':
'No available clusters. Please create a cluster before adding a node.' 'No available clusters. Please create a cluster before adding a node.',
'resources.metrics.details': 'Metrics Details'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -115,5 +116,6 @@ export default {
// 5. 'resources.worker.maintenance.remark': 'Remark', // 5. 'resources.worker.maintenance.remark': 'Remark',
// 6. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks', // 6. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks',
// 7. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.', // 7. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
// 8. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.' // 8. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.',
// 9. 'resources.metrics.details': 'Metrics Details'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2 -1
View File
@@ -102,5 +102,6 @@ export default {
'resources.worker.maintenance.remark.rules': '请输入维护备注', 'resources.worker.maintenance.remark.rules': '请输入维护备注',
'resources.worker.maintenance.tips': 'resources.worker.maintenance.tips':
'进入维护模式后,节点将停止调度新的模型实例部署任务,正在运行的实例不会受到影响。', '进入维护模式后,节点将停止调度新的模型实例部署任务,正在运行的实例不会受到影响。',
'resources.worker.noCluster.tips': '当前无可用集群,请先创建集群再添加节点。' 'resources.worker.noCluster.tips': '当前无可用集群,请先创建集群再添加节点。',
'resources.metrics.details': '监控详情'
}; };
@@ -11,6 +11,7 @@ import { HandlerOptions } from '@/hooks/use-chunk-fetch';
import useDownloadStream from '@/hooks/use-download-stream'; import useDownloadStream from '@/hooks/use-download-stream';
import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark'; import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark';
import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link';
import { convertFileSize } from '@/utils'; import { convertFileSize } from '@/utils';
import { import {
DeleteOutlined, DeleteOutlined,
@@ -327,6 +328,12 @@ const childActionList = [
], ],
icon: <IconFont type="icon-logs" /> icon: <IconFont type="icon-logs" />
}, },
{
label: 'resources.metrics.details',
key: 'metrics',
status: [InstanceStatusMap.Running],
icon: <IconFont type="icon-metrics" />
},
{ {
label: 'common.button.downloadLog', label: 'common.button.downloadLog',
key: 'download', key: 'download',
@@ -428,6 +435,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
defaultOpenId, defaultOpenId,
handleChildSelect handleChildSelect
}) => { }) => {
const { goToGrafana } = useGranfanaLink({ type: 'instance' });
const { runBenchmarkOnInstance } = useBenchmarkTargetInstance(); const { runBenchmarkOnInstance } = useBenchmarkTargetInstance();
const [api, contextHolder] = notification.useNotification({ const [api, contextHolder] = notification.useNotification({
stack: { threshold: 1 } stack: { threshold: 1 }
@@ -689,6 +697,8 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
filename: createFileName(instanceData.name), filename: createFileName(instanceData.name),
downloadNotification downloadNotification
}); });
} else if (val === 'metrics') {
goToGrafana(instanceData);
} else { } else {
handleChildSelect(val, instanceData); handleChildSelect(val, instanceData);
} }
+7 -1
View File
@@ -9,7 +9,6 @@ import SealTable from '@/components/seal-table';
import { TableOrder } from '@/components/seal-table/types'; import { TableOrder } from '@/components/seal-table/types';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings'; import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import { PageActionType } from '@/config/types';
import useBodyScroll from '@/hooks/use-body-scroll'; import useBodyScroll from '@/hooks/use-body-scroll';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableRowSelection from '@/hooks/use-table-row-selection';
@@ -18,6 +17,7 @@ import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
import { TargetStatusValueMap } from '@/pages/model-routes/config'; import { TargetStatusValueMap } from '@/pages/model-routes/config';
import useOpenPlayground from '@/pages/model-routes/hooks/use-open-playground'; import useOpenPlayground from '@/pages/model-routes/hooks/use-open-playground';
import useQueryTargets from '@/pages/model-routes/services/use-query-targets'; import useQueryTargets from '@/pages/model-routes/services/use-query-targets';
import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link';
import { handleBatchRequest } from '@/utils'; import { handleBatchRequest } from '@/utils';
import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'; import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max'; import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
@@ -170,6 +170,9 @@ const Models: React.FC<ModelsProps> = ({
useEffect(() => { useEffect(() => {
fetchTargets({}); fetchTargets({});
}, []); }, []);
const { goToGrafana } = useGranfanaLink({
type: 'model'
});
const [openLogModal, setOpenLogModal] = useState(false); const [openLogModal, setOpenLogModal] = useState(false);
const [openDeployModal, setOpenDeployModal] = useState<{ const [openDeployModal, setOpenDeployModal] = useState<{
@@ -451,6 +454,9 @@ const Models: React.FC<ModelsProps> = ({
name: targetRoute?.route_name || '' name: targetRoute?.route_name || ''
}); });
} }
if (val === 'metrics') {
goToGrafana(row);
}
} catch (error) { } catch (error) {
// ignore // ignore
} }
@@ -39,6 +39,11 @@ export const ActionList: ActionItem[] = [
key: 'edit', key: 'edit',
icon: icons.EditOutlined icon: icons.EditOutlined
}, },
{
label: 'resources.metrics.details',
key: 'metrics',
icon: icons.Metrics
},
{ {
label: 'models.openinplayground', label: 'models.openinplayground',
key: 'chat', key: 'chat',
-1
View File
@@ -35,7 +35,6 @@ const Models: React.FC<{ clusterId?: number }> = ({ clusterId }) => {
loadend: false, loadend: false,
total: 0 total: 0
}); });
const chunkRequedtRef = useRef<any>(); const chunkRequedtRef = useRef<any>();
const chunkInstanceRequedtRef = useRef<any>(); const chunkInstanceRequedtRef = useRef<any>();
const isPageHidden = useRef(false); const isPageHidden = useRef(false);
+4 -5
View File
@@ -8,6 +8,7 @@ import { DockerStepsFromWorker } from '@/pages/cluster-management/components/add
import { ClusterListItem } from '@/pages/cluster-management/config/types'; import { ClusterListItem } from '@/pages/cluster-management/config/types';
import useAddWorker from '@/pages/cluster-management/hooks/use-add-worker'; import useAddWorker from '@/pages/cluster-management/hooks/use-add-worker';
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result'; import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Table, message } from 'antd'; import { ConfigProvider, Table, message } from 'antd';
@@ -20,7 +21,6 @@ import {
updateWorker updateWorker
} from '../apis'; } from '../apis';
import { ListItem } from '../config/types'; import { ListItem } from '../config/types';
import useTerminalTabs from '../hooks/use-terminal-tabs';
import useWorkerColumns from '../hooks/use-worker-columns'; import useWorkerColumns from '../hooks/use-worker-columns';
import useWorkerMaintenance from '../hooks/use-worker-maintenance'; import useWorkerMaintenance from '../hooks/use-worker-maintenance';
import UpdateLabels from './update-labels'; import UpdateLabels from './update-labels';
@@ -65,7 +65,7 @@ const Workers: React.FC<{
cluster_id: clusterId cluster_id: clusterId
} }
}); });
const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs(); const { goToGrafana } = useGranfanaLink({ type: 'worker' });
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } = const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
useWorkerMaintenance({ fetchData: handleSearch }); useWorkerMaintenance({ fetchData: handleSearch });
@@ -199,9 +199,8 @@ const Workers: React.FC<{
if (val === 'stop_maintenance') { if (val === 'stop_maintenance') {
handleStopMaintenance(record); handleStopMaintenance(record);
} }
if (val === 'metrics') {
if (val === 'terminal') { goToGrafana(record);
handleAddTerminal(record);
} }
}); });
@@ -0,0 +1,42 @@
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
const useGranfanaLink = (options: {
type: 'model' | 'worker' | 'instance';
}) => {
const goToModelGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/models/${row.id}/dashboard`,
'_blank'
);
};
const goToWorkerGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/workers/${row.id}/dashboard`,
'_blank'
);
};
const goToInstanceGrafana = (row: { id: number }) => {
window.open(
`/${GPUSTACK_API_BASE_URL}/model-instances/${row.id}/dashboard`,
'_blank'
);
};
const goToGrafana = (row: { id: number }) => {
if (options.type === 'model') {
goToModelGrafana(row);
} else if (options.type === 'worker') {
goToWorkerGrafana(row);
} else if (options.type === 'instance') {
goToInstanceGrafana(row);
}
};
return {
goToGrafana
};
};
export default useGranfanaLink;
@@ -1,5 +1,6 @@
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons'; import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
import LabelsCell from '@/components/label-cell'; import LabelsCell from '@/components/label-cell';
import ProgressBar from '@/components/progress-bar'; import ProgressBar from '@/components/progress-bar';
import InfoColumn from '@/components/simple-table/info-column'; import InfoColumn from '@/components/simple-table/info-column';
@@ -42,6 +43,11 @@ const IPWrapper = styled.span`
const ActionList = [ const ActionList = [
{ label: 'common.button.edit', key: 'edit', icon: <EditOutlined /> }, { label: 'common.button.edit', key: 'edit', icon: <EditOutlined /> },
{
label: 'resources.metrics.details',
key: 'metrics',
icon: <IconFont type="icon-metrics" />
},
// { // {
// label: 'common.button.detail', // label: 'common.button.detail',
// key: 'details', // key: 'details',