This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-uniffle.git
The following commit(s) were added to refs/heads/master by this push:
new 258db12f5 [#2288] feat(dashboard): Show nodeInfo in excludeNode page
(#2289)
258db12f5 is described below
commit 258db12f57aaa41c285b8336306a688efbd1d936
Author: kqhzz <[email protected]>
AuthorDate: Wed Dec 18 10:06:55 2024 +0800
[#2288] feat(dashboard): Show nodeInfo in excludeNode page (#2289)
### What changes were proposed in this pull request?
Show nodeInfo in excludeNode page
<img width="1593" alt="image"
src="https://github.com/user-attachments/assets/e74b23dc-4c32-4faf-94c7-278a0b05fef1"
/>
<img width="1670" alt="企业微信截图_b08453cf-21af-43ed-bcdd-4238390c9823"
src="https://github.com/user-attachments/assets/3b9ada0b-56ed-4515-9c24-0414ea324a27"
/>
### Why are the changes needed?
Fix: #2288
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
Manually.
---
.../coordinator/web/resource/ServerResource.java | 11 +-
.../main/webapp/src/pages/ShuffleServerPage.vue | 12 +-
.../src/pages/serverstatus/ExcludeNodeList.vue | 264 ---------------------
.../webapp/src/pages/serverstatus/NodeListPage.vue | 174 +++++++++++++-
dashboard/src/main/webapp/src/router/index.js | 3 +-
5 files changed, 185 insertions(+), 279 deletions(-)
diff --git
a/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/ServerResource.java
b/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/ServerResource.java
index ade673112..e86b6597d 100644
---
a/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/ServerResource.java
+++
b/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/ServerResource.java
@@ -79,8 +79,8 @@ public class ServerResource extends BaseResource {
serverList = clusterManager.getLostServerList();
} else if (ServerStatus.EXCLUDED.name().equalsIgnoreCase(status)) {
serverList =
- clusterManager.getExcludedNodes().stream()
- .map(ServerNode::new)
+ clusterManager.list().stream()
+ .filter(node ->
clusterManager.getExcludedNodes().contains(node.getId()))
.collect(Collectors.toList());
} else {
List<ServerNode> serverAllList = clusterManager.list();
@@ -92,9 +92,10 @@ public class ServerResource extends BaseResource {
serverList =
serverList.stream()
.filter(
- server -> {
- return status == null ||
server.getStatus().name().equalsIgnoreCase(status);
- })
+ server ->
+ status == null
+ || server.getStatus().name().equalsIgnoreCase(status)
+ ||
ServerStatus.EXCLUDED.name().equalsIgnoreCase(status))
.collect(Collectors.toList());
serverList.sort(Comparator.comparing(ServerNode::getId));
return Response.success(serverList);
diff --git a/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
b/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
index 43d23ee3e..3ecf7b1a0 100644
--- a/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
+++ b/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
@@ -21,7 +21,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/activeNodeList"
+ :to="{ path: '/shuffleserverpage/activeNodeList', query: {
isExcludedPage: false } }"
@click.native="routerHandler"
>
<el-card class="box-card" shadow="hover">
@@ -37,7 +37,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/decommissioningNodeList"
+ :to="{ path: '/shuffleserverpage/decommissioningNodeList', query: {
isExcludedPage: false } }"
@click.native="routerHandler"
>
<el-card class="box-card" shadow="hover">
@@ -55,7 +55,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/decommissionedNodeList"
+ :to="{ path: '/shuffleserverpage/decommissionedNodeList', query: {
isExcludedPage: false } }"
@click.native="routerHandler"
>
<el-card class="box-card" shadow="hover">
@@ -73,7 +73,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/lostNodeList"
+ :to="{ path: '/shuffleserverpage/lostNodeList', query: {
isExcludedPage: false } }"
@click.native="routerHandler"
:updateTotalPage="updateTotalPage"
>
@@ -90,7 +90,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/unhealthyNodeList"
+ :to="{ path: '/shuffleserverpage/unhealthyNodeList', query: {
isExcludedPage: false } }"
@click.native="routerHandler"
>
<el-card class="box-card" shadow="hover">
@@ -106,7 +106,7 @@
<el-col :span="4">
<router-link
class="router-link-active"
- to="/shuffleserverpage/excludeNodeList"
+ :to="{ path: '/shuffleserverpage/excludeNodeList', query: {
isExcludedPage: true } }"
@click.native="routerHandler"
>
<el-card class="box-card" shadow="hover">
diff --git
a/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
b/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
deleted file mode 100644
index e12768fdd..000000000
--- a/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
+++ /dev/null
@@ -1,264 +0,0 @@
-<!--
- ~ Licensed to the Apache Software Foundation (ASF) under one or more
- ~ contributor license agreements. See the NOTICE file distributed with
- ~ this work for additional information regarding copyright ownership.
- ~ The ASF licenses this file to You under the Apache License, Version 2.0
- ~ (the "License"); you may not use this file except in compliance with
- ~ the License. You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<template>
- <div>
- <div class="button-wrapper">
- <el-button type="success" @click="dialogFormVisible = true"> Add Node
</el-button>
- <el-button type="danger" @click="handleDeleteNode">Delete({{
selectItemNum }})</el-button>
- </div>
- <el-divider />
- <div>
- <el-table
- :data="filteredTableData"
- :row-key="rowKey"
- :default-sort="sortColumn"
- @sort-change="sortChangeEvent"
- @selection-change="handlerSelectionChange"
- class="table-wapper"
- ref="table"
- stripe
- >
- <el-table-column type="selection" width="55" />
- <el-table-column prop="id" label="ExcludeNodeId" min-width="180"
:sortable="true" />
- <el-table-column align="right">
- <template #header>
- <el-input v-model="searchKeyword" size="small" placeholder="Type
to search" />
- </template>
- </el-table-column>
- </el-table>
- </div>
- <div>
- <el-dialog
- v-model="dialogFormVisible"
- title="Please enter the server id list to be excluded:"
- class="dialog-wrapper"
- >
- <el-form>
- <el-form-item :label-width="formLabelWidth">
- <el-input
- v-model="textarea"
- class="textarea-wrapper"
- :rows="10"
- type="textarea"
- placeholder="Please input"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="dialogFormVisible = false">Cancel</el-button>
- <el-button type="primary" @click="handleConfirmAddHandler">
Confirm </el-button>
- </div>
- </template>
- </el-dialog>
- </div>
- </div>
-</template>
-<script>
-import { onMounted, reactive, ref, inject, computed } from 'vue'
-import {
- addShuffleExcludeNodes,
- getShuffleExcludeNodes,
- removeShuffleExcludeNodes
-} from '@/api/api'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-export default {
- setup() {
- const pageData = reactive({ tableData: [] })
- const currentServerStore = useCurrentServerStore()
-
- const dialogFormVisible = ref(false)
- const formLabelWidth = '10%'
- const textarea = ref('')
-
- /**
- * Get the callback method of the parent page and update the number of
servers on the page.
- */
- const updateTotalPage = inject('updateTotalPage')
-
- async function getShuffleExcludeNodesPage() {
- const res = await getShuffleExcludeNodes()
- pageData.tableData = res.data.data
- }
-
- // The system obtains data from global variables and requests the
interface to obtain new data after data changes.
- currentServerStore.$subscribe((mutable, state) => {
- if (state.currentServer) {
- getShuffleExcludeNodesPage()
- }
- })
-
- onMounted(() => {
- // If the coordinator address to request is not found in the global
variable, the request is not initiated.
- if (currentServerStore.currentServer) {
- getShuffleExcludeNodesPage()
- }
- })
-
- /**
- * The following describes how to handle sort events.
- */
- const sortColumn = reactive({})
- const sortChangeEvent = (sortInfo) => {
- for (const sortColumnKey in sortColumn) {
- delete sortColumn[sortColumnKey]
- }
- sortColumn[sortInfo.prop] = sortInfo.order
- }
-
- /**
- * The following describes how to handle add events.
- */
- function handleConfirmAddHandler() {
- dialogFormVisible.value = false
- addShuffleExcludeNodesPage()
- // Refreshing the number of blacklists.
- updateTotalPage()
- // Refreshing the Blacklist list.
- getShuffleExcludeNodesPage()
- }
-
- async function addShuffleExcludeNodesPage() {
- try {
- const excludeNodes = textarea.value.split('\n').map((item) =>
item.trim())
- const excludeNodesObj = { excludeNodes }
- const res = await addShuffleExcludeNodes(excludeNodesObj)
- if (res.status >= 200 && res.status < 300) {
- if (res.data.data === 'success') {
- ElMessage.success('Add successfully.')
- } else {
- ElMessage.error('Add failed.')
- }
- } else {
- ElMessage.error('Failed to add due to server bad.')
- }
- } catch (err) {
- ElMessage.error('Failed to add due to network exception.')
- }
- }
-
- /**
- * The following describes how to handle blacklist deletion events.
- */
- const selectItemNum = ref(0)
- const rowKey = 'id'
- const selectedRows = ref([])
- function handlerSelectionChange(selection) {
- selectedRows.value = selection
- selectItemNum.value = selectedRows.value.length
- }
- function handleDeleteNode() {
- ElMessageBox.confirm('Are you sure about removing these nodes?',
'Warning', {
- confirmButtonText: 'OK',
- cancelButtonText: 'Cancel',
- type: 'warning'
- })
- .then(() => {
- if (selectedRows.value.length === 0) {
- ElMessage({
- type: 'info',
- message: 'No node is selected, Nothing!'
- })
- } else {
- const selectedIds = selectedRows.value.map((row) => row[rowKey])
- // pageData.tableData = pageData.tableData.filter(row =>
!selectedIds.includes(row[rowKey]));
- deleteShuffleExcludedNodes(selectedIds)
- // Refreshing the number of blacklists.
- updateTotalPage()
- // Refreshing the Blacklist list.
- getShuffleExcludeNodesPage()
- ElMessage({
- type: 'success',
- message: 'Delete completed'
- })
- }
- })
- .catch(() => {
- ElMessage({
- type: 'info',
- message: 'Delete canceled'
- })
- })
- }
-
- async function deleteShuffleExcludedNodes(excludeNodes) {
- try {
- const excludeNodesObj = { excludeNodes }
- const res = await removeShuffleExcludeNodes(excludeNodesObj)
- if (res.status >= 200 && res.status < 300) {
- if (res.data.data === 'success') {
- ElMessage.success('Add successfully.')
- } else {
- ElMessage.error('Add failed.')
- }
- } else {
- ElMessage.error('Failed to add due to server bad.')
- }
- } catch (err) {
- ElMessage.error('Failed to add due to network exception.')
- }
- }
-
- /**
- * The following describes how to handle blacklist select events.
- */
- const searchKeyword = ref('')
- const filteredTableData = computed(() => {
- const keyword = searchKeyword.value.trim()
- if (!keyword) {
- return pageData.tableData
- } else {
- return pageData.tableData.filter((row) => {
- return row.id.includes(keyword)
- })
- }
- })
- return {
- pageData,
- sortColumn,
- selectItemNum,
- sortChangeEvent,
- handleConfirmAddHandler,
- handleDeleteNode,
- handlerSelectionChange,
- dialogFormVisible,
- formLabelWidth,
- textarea,
- rowKey,
- searchKeyword,
- filteredTableData
- }
- }
-}
-</script>
-
-<style>
-.textarea-wrapper {
- width: 90%;
-}
-.dialog-wrapper {
- width: 50%;
-}
-.table-wapper {
- height: 550px;
- width: 100%;
- text-align: right;
-}
-</style>
diff --git a/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
index ac92a1b8a..9af0682fa 100644
--- a/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
+++ b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
@@ -17,13 +17,18 @@
<template>
<div>
+ <el-button type="success" @click="dialogFormVisible = true"
v-show="isExcludePage()"> Add Node </el-button>
+ <el-button type="danger" @click="handleDeleteNode"
v-show="isExcludePage()">Delete({{ selectItemNum }})</el-button>
<el-table
:data="listPageData.tableData"
height="800"
style="width: 100%"
+ :row-key="rowKey"
:default-sort="sortColumn"
@sort-change="sortChangeEvent"
+ @selection-change="handlerSelectionChange"
>
+ <el-table-column type="selection" width="55" v-show="isExcludePage()"/>
<el-table-column prop="id" label="Id" min-width="140" sortable fixed />
<el-table-column label="NodeInfo(ip:jetty/grpc/netty)" min-width="140" >
<template v-slot="{ row }">
@@ -109,6 +114,30 @@
</template>
</el-table-column>
</el-table>
+ <el-dialog
+ v-model="dialogFormVisible"
+ title="Please enter the server id list to be excluded:"
+ class="dialog-wrapper"
+ v-show="isExcludePage()"
+ >
+ <el-form>
+ <el-form-item :label-width="formLabelWidth">
+ <el-input
+ v-model="textarea"
+ class="textarea-wrapper"
+ :rows="10"
+ type="textarea"
+ placeholder="Please input"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="dialogFormVisible = false">Cancel</el-button>
+ <el-button type="primary" @click="handleConfirmAddHandler"> Confirm
</el-button>
+ </div>
+ </template>
+ </el-dialog>
</div>
</template>
<script>
@@ -127,7 +156,10 @@ import {
getShuffleServerConf,
getShuffleServerMetrics,
getShuffleServerPrometheusMetrics,
- getShuffleServerStacks
+ getShuffleServerStacks,
+ addShuffleExcludeNodes,
+ getShuffleExcludeNodes,
+ removeShuffleExcludeNodes
} from '@/api/api'
export default {
@@ -137,6 +169,114 @@ export default {
const sortColumn = reactive({})
const listPageData = reactive({ tableData: [] })
const isShowRemove = ref(false)
+ const dialogFormVisible = ref(false)
+ const formLabelWidth = '10%'
+ const textarea = ref('')
+ const selectItemNum = ref(0)
+ const rowKey = 'id'
+ const selectedRows = ref([])
+ function isExcludePage() {
+ return router.currentRoute.value.query.isExcludedPage === 'true';
+ }
+
+ async function getShuffleExcludeNodesPage() {
+ const res = await getShuffleExcludeNodes()
+ listPageData.tableData = res.data.data
+ }
+
+ // The system obtains data from global variables and requests the
interface to obtain new data after data changes.
+ currentServerStore.$subscribe((mutable, state) => {
+ if (state.currentServer) {
+ getShuffleExcludeNodesPage()
+ }
+ })
+
+ onMounted(() => {
+ // If the coordinator address to request is not found in the global
variable, the request is not initiated.
+ if (currentServerStore.currentServer) {
+ getShuffleExcludeNodesPage()
+ }
+ })
+
+ /**
+ * The following describes how to handle add events.
+ */
+ function handleConfirmAddHandler() {
+ dialogFormVisible.value = false
+ addShuffleExcludeNodesPage()
+ // Refreshing the number of blacklists.
+ updateTotalPage()
+ // Refreshing the Blacklist list.
+ getShuffleExcludeNodesPage()
+ }
+ async function addShuffleExcludeNodesPage() {
+ try {
+ const excludeNodes = textarea.value.split('\n').map((item) =>
item.trim())
+ const excludeNodesObj = { excludeNodes }
+ const res = await addShuffleExcludeNodes(excludeNodesObj)
+ if (res.status >= 200 && res.status < 300) {
+ if (res.data.data === 'success') {
+ ElMessage.success('Add successfully.')
+ } else {
+ ElMessage.error('Add failed.')
+ }
+ } else {
+ ElMessage.error('Failed to add due to server bad.')
+ }
+ } catch (err) {
+ ElMessage.error('Failed to add due to network exception.')
+ }
+ }
+ function handleDeleteNode() {
+ ElMessageBox.confirm('Are you sure about removing these nodes?',
'Warning', {
+ confirmButtonText: 'OK',
+ cancelButtonText: 'Cancel',
+ type: 'warning'
+ })
+ .then(() => {
+ if (selectedRows.value.length === 0) {
+ ElMessage({
+ type: 'info',
+ message: 'No node is selected, Nothing!'
+ })
+ } else {
+ const selectedIds = selectedRows.value.map((row) => row[rowKey])
+ deleteShuffleExcludedNodes(selectedIds)
+ // Refreshing the number of blacklists.
+ updateTotalPage()
+ // Refreshing the Blacklist list.
+ getShuffleExcludeNodesPage()
+ ElMessage({
+ type: 'success',
+ message: 'Delete completed'
+ })
+ }
+ })
+ .catch(() => {
+ ElMessage({
+ type: 'info',
+ message: 'Delete canceled'
+ })
+ })
+ }
+
+ async function deleteShuffleExcludedNodes(excludeNodes) {
+ try {
+ const excludeNodesObj = { excludeNodes }
+ const res = await removeShuffleExcludeNodes(excludeNodesObj)
+ if (res.status >= 200 && res.status < 300) {
+ if (res.data.data === 'success') {
+ ElMessage.success('Delete successfully.')
+ } else {
+ ElMessage.error('Delete failed.')
+ }
+ } else {
+ ElMessage.error('Failed to delete due to server bad.')
+ }
+ } catch (err) {
+ ElMessage.error('Failed to delete due to network exception.')
+ }
+ }
async function deleteLostServer(row) {
try {
const params = { serverId: row.id }
@@ -177,6 +317,11 @@ export default {
listPageData.tableData = res.data.data
}
+ async function getShuffleExcludeListPage() {
+ const res = await getShuffleExcludeNodes()
+ listPageData.tableData = res.data.data
+ }
+
function combinedRequestAddress(serverRow) {
return 'http://' + serverRow.ip + ':' + serverRow.jettyPort
}
@@ -279,6 +424,8 @@ export default {
} else if (router.currentRoute.value.name === 'lostNodeList') {
isShowRemove.value = true
getShuffleLostListPage()
+ } else if (router.currentRoute.value.name === 'excludeNodeList') {
+ getShuffleExcludeListPage()
}
}
@@ -324,6 +471,10 @@ export default {
// Cancelled
})
}
+ function handlerSelectionChange(selection) {
+ selectedRows.value = selection
+ selectItemNum.value = selectedRows.value.length
+ }
return {
listPageData,
sortColumn,
@@ -335,8 +486,27 @@ export default {
handlerServerMetrics,
handlerServerStacks,
memFormatter,
- dateFormatter
+ dateFormatter,
+ dialogFormVisible,
+ formLabelWidth,
+ textarea,
+ selectItemNum,
+ handleDeleteNode,
+ getShuffleExcludeNodesPage,
+ handleConfirmAddHandler,
+ isExcludePage,
+ rowKey,
+ handlerSelectionChange
}
}
}
</script>
+
+<style>
+.textarea-wrapper {
+ width: 90%;
+}
+.dialog-wrapper {
+ width: 50%;
+}
+</style>
\ No newline at end of file
diff --git a/dashboard/src/main/webapp/src/router/index.js
b/dashboard/src/main/webapp/src/router/index.js
index f0b6bb8e7..114ccdb3f 100644
--- a/dashboard/src/main/webapp/src/router/index.js
+++ b/dashboard/src/main/webapp/src/router/index.js
@@ -20,7 +20,6 @@ import ApplicationPage from '@/pages/ApplicationPage.vue'
import DashboardPage from '@/pages/DashboardPage.vue'
import CoordinatorServerPage from '@/pages/CoordinatorServerPage.vue'
import ShuffleServerPage from '@/pages/ShuffleServerPage.vue'
-import ExcludeNodeList from '@/pages/serverstatus/ExcludeNodeList'
import NodeListPage from '@/pages/serverstatus/NodeListPage.vue'
const routes = [
@@ -68,7 +67,7 @@ const routes = [
{
path: '/shuffleserverpage/excludeNodeList',
name: 'excludeNodeList',
- component: ExcludeNodeList
+ component: NodeListPage
}
]
},