You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ch...@apache.org on 2023/04/10 05:16:41 UTC

[kyuubi] branch master updated: [KYUUBI #3654][UI] Add Engine Manager Page

This is an automated email from the ASF dual-hosted git repository.

chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git


The following commit(s) were added to refs/heads/master by this push:
     new f5ef4018e [KYUUBI #3654][UI] Add Engine Manager Page
f5ef4018e is described below

commit f5ef4018eeb1b6495afbc48dcfe7b2f004e8abce
Author: zwangsheng <22...@qq.com>
AuthorDate: Mon Apr 10 13:16:20 2023 +0800

    [KYUUBI #3654][UI] Add Engine Manager Page
    
    ### _Why are the changes needed?_
    
    Close #3654
    
    Add Engine Manager Page for UI
    
    ### _How was this patch tested?_
    - [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible
    
    - [x] Add screenshots for manual tests if appropriate
    
    - [ ] [Run test](https://kyuubi.readthedocs.io/en/master/develop_tools/testing.html#running-tests) locally before make a pull request
    
    ![popo_2023-04-07  14-19-01](https://user-images.githubusercontent.com/52876270/230553293-a935533c-792f-47e6-9c3d-e91bf469452e.jpg)
    
    Closes #4674 from zwangsheng/KYUUBI_3654.
    
    Closes #3654
    
    b18c7d2d9 [zwangsheng] fix style
    75b61350a [zwangsheng] fix style
    a064203fc [zwangsheng] I18n
    da61ea2fe [zwangsheng] [KYUUBI #3654][UI] Engine Manager Page
    
    Authored-by: zwangsheng <22...@qq.com>
    Signed-off-by: Cheng Pan <ch...@apache.org>
---
 .../web-ui/src/{router => api/engine}/index.ts     |  50 ++-----
 .../src/{router/index.ts => api/engine/types.ts}   |  42 +-----
 kyuubi-server/web-ui/src/locales/en_US/index.ts    |  18 ++-
 kyuubi-server/web-ui/src/locales/zh_CN/index.ts    |  18 ++-
 .../web-ui/src/router/{ => engine}/index.ts        |  35 +----
 kyuubi-server/web-ui/src/router/index.ts           |   2 +
 .../src/{router/index.ts => utils/engine.ts}       |  41 +----
 kyuubi-server/web-ui/src/views/engine/index.vue    | 166 +++++++++++++++++++++
 .../src/views/layout/components/aside/types.ts     |  10 ++
 .../views/operation/operation-statistics/index.vue |  10 +-
 .../src/views/session/session-statistics/index.vue |   6 +-
 11 files changed, 248 insertions(+), 150 deletions(-)

diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/api/engine/index.ts
similarity index 52%
copy from kyuubi-server/web-ui/src/router/index.ts
copy to kyuubi-server/web-ui/src/api/engine/index.ts
index 4d01da552..ff6dc038d 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/api/engine/index.ts
@@ -15,39 +15,21 @@
  * limitations under the License.
  */
 
-import { createRouter, createWebHistory } from 'vue-router'
-import overviewRoutes from './overview'
-import workloadRoutes from './workload'
-import operationRoutes from './operation'
-import contactRoutes from './contact'
-import sessionRoutes from './session'
+import request from '@/utils/request'
+import { IEngineSearch } from './types'
 
-const routes = [
-  {
-    path: '/',
-    name: 'main',
-    redirect: {
-      name: 'layout'
-    }
-  },
-  {
-    path: '/layout',
-    name: 'layout',
-    component: () => import('@/views/layout/index.vue'),
-    redirect: 'overview',
-    children: [
-      ...overviewRoutes,
-      ...sessionRoutes,
-      ...workloadRoutes,
-      ...operationRoutes,
-      ...contactRoutes
-    ]
-  }
-]
+export function getAllEngines(params: IEngineSearch) {
+  return request({
+    url: 'api/v1/admin/engine',
+    method: 'get',
+    params
+  })
+}
 
-const router = createRouter({
-  history: createWebHistory('/ui'),
-  routes
-})
-
-export default router
+export function deleteEngine(params: IEngineSearch) {
+  return request({
+    url: 'api/v1/admin/engine',
+    method: 'delete',
+    params
+  })
+}
diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/api/engine/types.ts
similarity index 52%
copy from kyuubi-server/web-ui/src/router/index.ts
copy to kyuubi-server/web-ui/src/api/engine/types.ts
index 4d01da552..86a05dd29 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/api/engine/types.ts
@@ -15,39 +15,11 @@
  * limitations under the License.
  */
 
-import { createRouter, createWebHistory } from 'vue-router'
-import overviewRoutes from './overview'
-import workloadRoutes from './workload'
-import operationRoutes from './operation'
-import contactRoutes from './contact'
-import sessionRoutes from './session'
+interface IEngineSearch {
+  type: null | string
+  sharelevel: null | string
+  'hive.server2.proxy.user': null | string
+  subdomain?: null | string
+}
 
-const routes = [
-  {
-    path: '/',
-    name: 'main',
-    redirect: {
-      name: 'layout'
-    }
-  },
-  {
-    path: '/layout',
-    name: 'layout',
-    component: () => import('@/views/layout/index.vue'),
-    redirect: 'overview',
-    children: [
-      ...overviewRoutes,
-      ...sessionRoutes,
-      ...workloadRoutes,
-      ...operationRoutes,
-      ...contactRoutes
-    ]
-  }
-]
-
-const router = createRouter({
-  history: createWebHistory('/ui'),
-  routes
-})
-
-export default router
+export { IEngineSearch }
diff --git a/kyuubi-server/web-ui/src/locales/en_US/index.ts b/kyuubi-server/web-ui/src/locales/en_US/index.ts
index d50f22915..dc1986939 100644
--- a/kyuubi-server/web-ui/src/locales/en_US/index.ts
+++ b/kyuubi-server/web-ui/src/locales/en_US/index.ts
@@ -23,15 +23,25 @@ export default {
   session_id: 'Session ID',
   operation_id: 'Operation ID',
   create_time: 'Create Time',
-  operation: 'Operation',
-  delete_confirm: 'Delete Confirm',
-  close_confirm: 'Close Confirm',
-  cancel_confirm: 'Cancel Confirm',
   start_time: 'State Time',
   complete_time: 'Completed Time',
   state: 'State',
   duration: 'Duration',
   statement: 'Statement',
+  engine_address: 'Engine Address',
+  engine_id: 'Engine ID',
+  engine_type: 'Engine Type',
+  share_level: 'Share Level',
+  version: 'Version',
+  operation: {
+    text: 'Operation',
+    delete_confirm: 'Delete Confirm',
+    close_confirm: 'Close Confirm',
+    cancel_confirm: 'Cancel Confirm',
+    close: 'Close',
+    cancel: 'Cancel',
+    delete: 'Delete'
+  },
   message: {
     delete_succeeded: 'Delete {name} Succeeded',
     delete_failed: 'Delete {name} Failed',
diff --git a/kyuubi-server/web-ui/src/locales/zh_CN/index.ts b/kyuubi-server/web-ui/src/locales/zh_CN/index.ts
index 443d129cc..87b15cc4d 100644
--- a/kyuubi-server/web-ui/src/locales/zh_CN/index.ts
+++ b/kyuubi-server/web-ui/src/locales/zh_CN/index.ts
@@ -23,15 +23,25 @@ export default {
   session_id: 'Session ID',
   operation_id: 'Operation ID',
   create_time: '创建时间',
-  operation: '操作',
-  delete_confirm: '确认删除',
-  close_confirm: '确认关闭',
-  cancel_confirm: '确认取消',
   start_time: '开始时间',
   complete_time: '完成时间',
   state: '状态',
   duration: '运行时间',
   statement: 'Statement',
+  engine_address: 'Engine 地址',
+  engine_id: 'Engine ID',
+  engine_type: 'Engine 类型',
+  share_level: '共享级别',
+  version: '版本',
+  operation: {
+    text: '操作',
+    delete_confirm: '确认删除',
+    close_confirm: '确认关闭',
+    cancel_confirm: '确认取消',
+    close: '关闭',
+    cancel: '取消',
+    delete: '删除'
+  },
   message: {
     delete_succeeded: '删除 {name} 成功',
     delete_failed: '删除 {name} 失败',
diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/router/engine/index.ts
similarity index 53%
copy from kyuubi-server/web-ui/src/router/index.ts
copy to kyuubi-server/web-ui/src/router/engine/index.ts
index 4d01da552..22b056a32 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/router/engine/index.ts
@@ -15,39 +15,12 @@
  * limitations under the License.
  */
 
-import { createRouter, createWebHistory } from 'vue-router'
-import overviewRoutes from './overview'
-import workloadRoutes from './workload'
-import operationRoutes from './operation'
-import contactRoutes from './contact'
-import sessionRoutes from './session'
-
 const routes = [
   {
-    path: '/',
-    name: 'main',
-    redirect: {
-      name: 'layout'
-    }
-  },
-  {
-    path: '/layout',
-    name: 'layout',
-    component: () => import('@/views/layout/index.vue'),
-    redirect: 'overview',
-    children: [
-      ...overviewRoutes,
-      ...sessionRoutes,
-      ...workloadRoutes,
-      ...operationRoutes,
-      ...contactRoutes
-    ]
+    path: '/engine/engine-statistics',
+    name: 'engine-statistics',
+    component: () => import('@/views/engine/index.vue')
   }
 ]
 
-const router = createRouter({
-  history: createWebHistory('/ui'),
-  routes
-})
-
-export default router
+export default routes
diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/router/index.ts
index 4d01da552..241cdf506 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/router/index.ts
@@ -21,6 +21,7 @@ import workloadRoutes from './workload'
 import operationRoutes from './operation'
 import contactRoutes from './contact'
 import sessionRoutes from './session'
+import engineRoutes from './engine'
 
 const routes = [
   {
@@ -40,6 +41,7 @@ const routes = [
       ...sessionRoutes,
       ...workloadRoutes,
       ...operationRoutes,
+      ...engineRoutes,
       ...contactRoutes
     ]
   }
diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/utils/engine.ts
similarity index 52%
copy from kyuubi-server/web-ui/src/router/index.ts
copy to kyuubi-server/web-ui/src/utils/engine.ts
index 4d01da552..da6646191 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/utils/engine.ts
@@ -15,39 +15,12 @@
  * limitations under the License.
  */
 
-import { createRouter, createWebHistory } from 'vue-router'
-import overviewRoutes from './overview'
-import workloadRoutes from './workload'
-import operationRoutes from './operation'
-import contactRoutes from './contact'
-import sessionRoutes from './session'
+function getEngineType() {
+  return ['SPARK_SQL', 'FLINK_SQL', 'TRINO', 'HIVE_SQL', 'JDBC']
+}
 
-const routes = [
-  {
-    path: '/',
-    name: 'main',
-    redirect: {
-      name: 'layout'
-    }
-  },
-  {
-    path: '/layout',
-    name: 'layout',
-    component: () => import('@/views/layout/index.vue'),
-    redirect: 'overview',
-    children: [
-      ...overviewRoutes,
-      ...sessionRoutes,
-      ...workloadRoutes,
-      ...operationRoutes,
-      ...contactRoutes
-    ]
-  }
-]
+function getShareLevel() {
+  return ['CONNECTION', 'USER', 'GROUP', 'SERVER']
+}
 
-const router = createRouter({
-  history: createWebHistory('/ui'),
-  routes
-})
-
-export default router
+export { getEngineType, getShareLevel }
diff --git a/kyuubi-server/web-ui/src/views/engine/index.vue b/kyuubi-server/web-ui/src/views/engine/index.vue
new file mode 100644
index 000000000..cecbde709
--- /dev/null
+++ b/kyuubi-server/web-ui/src/views/engine/index.vue
@@ -0,0 +1,166 @@
+<!--
+* 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>
+  <el-card :body-style="{ padding: '10px 14px' }" class="filter_card">
+    <header>
+      <el-space class="search-box">
+        <el-select
+          v-model="searchParam.type"
+          :placeholder="$t('engine_type')"
+          clearable
+          style="width: 210px"
+          @change="getList">
+          <el-option
+            v-for="item in getEngineType()"
+            :key="item"
+            :label="item"
+            :value="item" />
+        </el-select>
+        <el-select
+          v-model="searchParam.sharelevel"
+          :placeholder="$t('share_level')"
+          clearable
+          style="width: 210px"
+          @change="getList">
+          <el-option
+            v-for="item in getShareLevel()"
+            :key="item"
+            :label="item"
+            :value="item" />
+        </el-select>
+        <el-input
+          v-model="searchParam['hive.server2.proxy.user']"
+          :placeholder="$t('user')"
+          style="width: 210px"
+          @keyup.enter="getList" />
+        <el-button type="primary" icon="Search" @click="getList" />
+      </el-space>
+    </header>
+  </el-card>
+  <el-card class="table-container">
+    <el-table v-loading="loading" :data="tableData" style="width: 100%">
+      <el-table-column
+        prop="instance"
+        :label="$t('engine_address')"
+        min-width="20%" />
+      <el-table-column :label="$t('engine_id')" min-width="20%">
+        <template #default="scope">
+          <span>{{
+            scope.row.attributes && scope.row.attributes['kyuubi.engine.id']
+              ? scope.row.attributes['kyuubi.engine.id']
+              : '-'
+          }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="engineType"
+        :label="$t('engine_type')"
+        min-width="20%" />
+      <el-table-column
+        prop="sharelevel"
+        :label="$t('share_level')"
+        min-width="20%" />
+
+      <el-table-column prop="user" :label="$t('user')" min-width="15%" />
+      <el-table-column prop="version" :label="$t('version')" min-width="15%" />
+      <el-table-column fixed="right" :label="$t('operation.text')" width="120">
+        <template #default="scope">
+          <el-space wrap>
+            <el-popconfirm
+              :title="$t('operation.delete_confirm')"
+              @confirm="handleDeleteEngine(scope.row)">
+              <template #reference>
+                <span>
+                  <el-tooltip
+                    effect="dark"
+                    :content="$t('operation.delete')"
+                    placement="top">
+                    <template #default>
+                      <el-button type="danger" icon="Delete" circle />
+                    </template>
+                  </el-tooltip>
+                </span>
+              </template>
+            </el-popconfirm>
+          </el-space>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-card>
+</template>
+
+<script lang="ts" setup>
+  import { reactive } from 'vue'
+  import { getAllEngines, deleteEngine } from '@/api/engine'
+  import { IEngineSearch } from '@/api/engine/types'
+  import { useTable } from '@/views/common/use-table'
+  import { ElMessage } from 'element-plus'
+  import { useI18n } from 'vue-i18n'
+  import { getEngineType, getShareLevel } from '@/utils/engine'
+
+  const { t } = useI18n()
+  const { tableData, loading, getList: _getList } = useTable()
+  // default search params
+  const searchParam: IEngineSearch = reactive({
+    type: 'SPARK_SQL',
+    sharelevel: 'USER',
+    'hive.server2.proxy.user': 'anonymous'
+  })
+  const getList = () => {
+    _getList(getAllEngines, searchParam)
+  }
+  const init = () => {
+    getList()
+  }
+
+  function handleDeleteEngine(row: any) {
+    deleteEngine({
+      type: row?.engineType,
+      sharelevel: row?.sharelevel,
+      'hive.server2.proxy.user': row?.user,
+      subdomain: row?.subdomain
+    })
+      .then(() => {
+        ElMessage({
+          message: t('delete_succeeded', { name: 'engine' }),
+          type: 'success'
+        })
+      })
+      .catch(() => {
+        ElMessage({
+          message: t('delete_failed', { name: 'engine' }),
+          type: 'error'
+        })
+      })
+      .finally(() => {
+        getList()
+      })
+  }
+
+  init()
+</script>
+
+<style scoped lang="scss">
+  header {
+    display: flex;
+    justify-content: flex-end;
+  }
+  .filter_card {
+    margin-bottom: 10px;
+  }
+</style>
diff --git a/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts b/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts
index 4772c1a4e..72b150fa8 100644
--- a/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts
+++ b/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts
@@ -31,6 +31,16 @@ export const MENUS = [
       }
     ]
   },
+  {
+    label: 'Engine Management',
+    icon: 'List',
+    children: [
+      {
+        label: 'Engine Statistics',
+        router: '/engine/engine-statistics'
+      }
+    ]
+  },
   {
     label: 'Workload',
     icon: 'List',
diff --git a/kyuubi-server/web-ui/src/views/operation/operation-statistics/index.vue b/kyuubi-server/web-ui/src/views/operation/operation-statistics/index.vue
index ff6706c72..992257eb8 100644
--- a/kyuubi-server/web-ui/src/views/operation/operation-statistics/index.vue
+++ b/kyuubi-server/web-ui/src/views/operation/operation-statistics/index.vue
@@ -54,18 +54,18 @@
             : '-'
         }}</template>
       </el-table-column>
-      <el-table-column fixed="right" :label="$t('operation')" width="110">
+      <el-table-column fixed="right" :label="$t('operation.text')" width="110">
         <template #default="scope">
           <el-space wrap>
             <el-popconfirm
               v-if="!isTerminalState(scope.row.state)"
-              :title="$t('cancel_confirm')"
+              :title="$t('operation.cancel_confirm')"
               @confirm="handleOperate(scope.row.identifier, 'CANCEL')">
               <template #reference>
                 <span>
                   <el-tooltip
                     effect="dark"
-                    :content="$t('cancel')"
+                    :content="$t('operation.cancel')"
                     placement="top">
                     <template #default>
                       <el-button type="danger" icon="Remove" circle />
@@ -75,13 +75,13 @@
               </template>
             </el-popconfirm>
             <el-popconfirm
-              :title="$t('close_confirm')"
+              :title="$t('operation.close_confirm')"
               @confirm="handleOperate(scope.row.identifier, 'CLOSE')">
               <template #reference>
                 <span>
                   <el-tooltip
                     effect="dark"
-                    :content="$t('close')"
+                    :content="$t('operation.close')"
                     placement="top">
                     <template #default>
                       <el-button type="danger" icon="CircleClose" circle />
diff --git a/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue b/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue
index 40a9b7568..327664dd1 100644
--- a/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue
+++ b/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue
@@ -46,16 +46,16 @@
           }}
         </template>
       </el-table-column>
-      <el-table-column fixed="right" :label="$t('operation')">
+      <el-table-column fixed="right" :label="$t('operation.text')">
         <template #default="scope">
           <el-popconfirm
-            :title="$t('delete_confirm')"
+            :title="$t('operation.delete_confirm')"
             @confirm="handleDeleteSession(scope.row.identifier)">
             <template #reference>
               <span>
                 <el-tooltip
                   effect="dark"
-                  :content="$t('delete')"
+                  :content="$t('operation.delete')"
                   placement="top">
                   <template #default>
                     <el-button type="danger" icon="Delete" circle />