From fba2428e30b79712eee04e0cb5aefa2ce38fa405 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:42:54 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/assets/type0-service.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 python/assets/type0-service.yaml diff --git a/python/assets/type0-service.yaml b/python/assets/type0-service.yaml new file mode 100644 index 0000000..e69de29 From cb10f62c35d15593257bfdb326d8f1ce61f01455 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:43:22 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-dm/src/pages/ListPages/Training/TrainingPage.jsx | 0 app-dm/src/router/index.jsx | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app-dm/src/pages/ListPages/Training/TrainingPage.jsx mode change 100755 => 100644 app-dm/src/router/index.jsx diff --git a/app-dm/src/pages/ListPages/Training/TrainingPage.jsx b/app-dm/src/pages/ListPages/Training/TrainingPage.jsx old mode 100755 new mode 100644 diff --git a/app-dm/src/router/index.jsx b/app-dm/src/router/index.jsx old mode 100755 new mode 100644 From fadb4a4ee3fd3a39a93192028c321f92d7c769e2 Mon Sep 17 00:00:00 2001 From: wang <842355358@qq.com> Date: Mon, 1 Jul 2024 10:43:22 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9webssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- app-dm/package-lock.json | 82 ++++++++++++++++++- app-dm/package.json | 1 + app-dm/src/TeacherPages/TestManage/Test.tsx | 89 +++++++++++++++++++++ app-dm/src/router/index.jsx | 6 +- app-dm/src/setupProxy.js | 2 +- build.sh | 14 ++-- docker-compose.yml | 7 +- docker/base-dm/Dockerfile | 4 +- docker/base/Dockerfile | 2 + docker/ssh-server/Dockerfile | 27 +------ python/.devenv | 27 +++++++ python/.gitignore | 3 + python/Dockerfile | 10 ++- python/app.py | 83 ++++++++++++++++++- python/assets/type0-pod.yaml | 19 +++++ python/k8s_func.py | 85 ++++++++++++++++++++ python/requirements.txt | 6 +- python/student_func.py | 2 +- 19 files changed, 419 insertions(+), 54 deletions(-) create mode 100644 app-dm/src/TeacherPages/TestManage/Test.tsx create mode 100644 python/.devenv create mode 100644 python/.gitignore create mode 100644 python/assets/type0-pod.yaml create mode 100644 python/k8s_func.py diff --git a/.gitignore b/.gitignore index cdd94a1..7dc4f0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ .idea -node_modules -__pycache__ -public \ No newline at end of file +node_modules \ No newline at end of file diff --git a/app-dm/package-lock.json b/app-dm/package-lock.json index a462fec..0261786 100644 --- a/app-dm/package-lock.json +++ b/app-dm/package-lock.json @@ -38,6 +38,7 @@ "react-scripts": "^5.0.1", "recharts": "^2.12.7", "redux": "^5.0.1", + "socket.io-client": "^4.7.5", "stream": "^0.0.2", "swr": "^2.2.5", "tailwindcss": "^3.4.3", @@ -4009,6 +4010,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -5441,7 +5447,7 @@ }, "node_modules/@xterm/xterm": { "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/@xterm/xterm/-/xterm-5.5.0.tgz", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" }, "node_modules/@xtuc/ieee754": { @@ -7865,6 +7871,46 @@ "node": ">= 0.8" } }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.16.0", "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", @@ -16477,6 +16523,32 @@ "node": ">=8" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", @@ -18912,6 +18984,14 @@ "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/app-dm/package.json b/app-dm/package.json index 89c2d69..4053782 100644 --- a/app-dm/package.json +++ b/app-dm/package.json @@ -32,6 +32,7 @@ "react-scripts": "^5.0.1", "recharts": "^2.12.7", "redux": "^5.0.1", + "socket.io-client": "^4.7.5", "stream": "^0.0.2", "swr": "^2.2.5", "tailwindcss": "^3.4.3", diff --git a/app-dm/src/TeacherPages/TestManage/Test.tsx b/app-dm/src/TeacherPages/TestManage/Test.tsx new file mode 100644 index 0000000..cb17197 --- /dev/null +++ b/app-dm/src/TeacherPages/TestManage/Test.tsx @@ -0,0 +1,89 @@ +import { Terminal } from "@xterm/xterm"; +import "@xterm/xterm/css/xterm.css"; +import React, { useEffect, useState, useRef } from 'react'; +import { useLocation } from 'react-router-dom'; +import io, { Socket } from "socket.io-client"; + +function base64Decode(encoded: string): string { + return atob(encoded); +} + +export default function WebSSH() { + const location = useLocation(); + const terminalObj = useRef(null); + const [terminal, setTerminal] = useState(null); + const [socket, setSocket] = useState(null); + const [ip, setIp] = useState(""); + const [port, setPort] = useState(""); + const [password, setPassword] = useState(""); + + useEffect(() => { + const params = new URLSearchParams(location.search); + const encodedIp = params.get("ip"); + const encodedPort = params.get("port"); + const encodedPassword = params.get("password"); + + if (encodedIp && encodedPort && encodedPassword) { + setIp(base64Decode(encodedIp)); + setPort(base64Decode(encodedPort)); + setPassword(base64Decode(encodedPassword)); + } + }, [location]); + + useEffect(() => { + if (terminal === null) { + setTerminal(new Terminal()); + } + }, []); + + useEffect(() => { + if (terminal !== null) { + terminal.open(terminalObj.current as HTMLDivElement); + + const newSocket = io("ws://36.138.114.105:32087"); + setSocket(newSocket); + + terminal.onData((data) => { + newSocket.emit("input", data); + }); + + terminal.onSelectionChange(() => { + if (terminal.hasSelection()) { + navigator.clipboard.writeText(terminal.getSelection()); + } + }); + + setInterval(() => { + newSocket.emit("heartbeat"); + }, 1000); + + newSocket.on("output", (data: string) => { + terminal.write(data); + }); + + return () => { + newSocket.disconnect(); + }; + } + }, [terminal]); + + useEffect(() => { + if (socket && ip && port && password) { + socket.emit("connect_ssh", { ip, port, password }); + } + }, [socket, ip, port, password]); + + function pasteContent(event: React.MouseEvent) { + navigator.clipboard.readText().then((content) => { + socket?.emit("input", content); + }); + event.preventDefault(); + } + + return ( +
+
+
+
+ ); +} diff --git a/app-dm/src/router/index.jsx b/app-dm/src/router/index.jsx index b5e2aa3..b94d2d8 100644 --- a/app-dm/src/router/index.jsx +++ b/app-dm/src/router/index.jsx @@ -23,11 +23,12 @@ import ClassID from '../TeacherPages/classlistpages/classId'; import ManageTest from '../TeacherPages/TestManage/ManageTest'; import SendTest from '../TeacherPages/TestManage/SendTest'; import Marking from '../TeacherPages/MarkingPages/Marking'; -import SendTrain from '../TeacherPages/Trainmanage/SendTrain' -import TrainManage from '../TeacherPages/Trainmanage/Trainmanage' import StudentLink from '../TeacherPages/MarkingPages/StudentLink'; +import SendTrain from '../TeacherPages/SendTrain/SendTrain'; +import TrainManage from '../TeacherPages/SendTest/trainmanage'; +import Test from "../TeacherPages/teachermanagetest/Test.tsx" // 定义一个组件来包裹除了特定页面(exam)外的所有页面使其有导航 const MainLayout = ({ headerNav:HeaderNav }) => { return ( @@ -69,6 +70,7 @@ function App() { }> } /> + }/> {/* 页面使用单独的布局,不包含HeaderNav和HeaderNavTeacher */} } />{/* 练习-考试页面 */} diff --git a/app-dm/src/setupProxy.js b/app-dm/src/setupProxy.js index 4a40b4f..d6d9212 100644 --- a/app-dm/src/setupProxy.js +++ b/app-dm/src/setupProxy.js @@ -4,7 +4,7 @@ module.exports = function(app) { app.use( '/api', // 如果请求路径匹配'/api',则进行代理 createProxyMiddleware({ - target: 'http://127.0.0.1:5000', // 目标后端服务地址 + target: 'http://36.138.114.105:32087', // 目标后端服务地址 changeOrigin: true, }) ); diff --git a/build.sh b/build.sh index 029e22f..38c1c27 100644 --- a/build.sh +++ b/build.sh @@ -20,13 +20,14 @@ build_image() { # 推送镜像 push_image() { + local service_name=$1 read -p "是否要推送到《达梦启元云原生大数据平台》? [Y/N] " confirm if [[ $confirm =~ ^[yY]([eE][sS])?$ ]]; then - echo "tag" - docker tag "${current_dir}-flask-app" 36.138.114.105:31000/cnsof50011836/flask-app - echo "pushing" - docker push 36.138.114.105:31000/cnsof50011836/flask-app - echo "pushed" + echo "${service_name} tag" + docker tag "${service_name}" 36.138.114.105:31000/cnsof50011836/$service_name + echo "${service_name} pushing" + docker push 36.138.114.105:31000/cnsof50011836/$service_name + echo "${service_name} pushed" else echo "用户选择不推送镜像" fi @@ -43,7 +44,8 @@ main() { build_image build-frontend build_image flask-app - push_image + push_image flask-app + push_image base-dm echo "脚本结束" } diff --git a/docker-compose.yml b/docker-compose.yml index 7b095cd..4d5bc3e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,14 +23,11 @@ services: flask-app: build: ./python restart: always + image: flask-app depends_on: - base-dm ports: - '8000:8000' - healthcheck: - test: ["CMD-SHELL", "curl --silent --fail localhost:8000/flask-health-check || exit 1"] - interval: 10s - timeout: 10s - retries: 3 + - '8765:8765' command: gunicorn -w 3 -t 60 -b 0.0.0.0:8000 app:app diff --git a/docker/base-dm/Dockerfile b/docker/base-dm/Dockerfile index 1d30f8f..24689fc 100644 --- a/docker/base-dm/Dockerfile +++ b/docker/base-dm/Dockerfile @@ -6,8 +6,8 @@ RUN useradd -m -s /bin/bash dmdba \ WORKDIR /home/dmdba # 传入安装包并解压 -RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip -RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ +RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ +&& unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ && chown -R dmdba:dmdba /home/dmdba \ && rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ && 7z x dm8_20240408_x86_rh7_64.iso \ diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index f73ec32..c03eea3 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -9,6 +9,8 @@ RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.l && apt-get install -y python3.10-venv libssl-dev sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \ # 设置中文环境 && apt install -y $(check-language-support) \ +&& sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config \ +&& sudo sed -i 's/^UsePAM yes/UsePAM no/' /etc/ssh/sshd_config \ && echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \ && sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \ && locale-gen \ diff --git a/docker/ssh-server/Dockerfile b/docker/ssh-server/Dockerfile index 048c94f..a005bc5 100644 --- a/docker/ssh-server/Dockerfile +++ b/docker/ssh-server/Dockerfile @@ -1,26 +1 @@ -FROM ubuntu -ENV DEBIAN_FRONTEND=noninteractive -ENV LANG=zh_CN.UTF-8 -ENV LC_ALL=zh_CN.UTF-8 -# 基础软件安装 -RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list \ -&& apt-get -y update && apt-get -y upgrade \ -&& apt-get install -y sudo vim python3-pip ssh wget unzip p7zip* language-pack-zh-hans language-selector-common locales locales-all \ -# 设置中文环境 -&& apt install -y $(check-language-support) \ -&& echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen \ -&& sudo /usr/sbin/update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 \ -&& locale-gen \ -# 添加用户 -&& useradd -m -s /bin/bash dmdba \ -# 修改用户密码 -&& echo "dmdba:123456" | chpasswd -# 传入安装包并解压 -WORKDIR /home/dmdba -RUN wget https://download.dameng.com/eco/adapter/DM8/202405/dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip -RUN unzip dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ -&& chown -R dmdba:dmdba /home/dmdba \ -&& rm -f dm8_20240408_x86_rh7_64_ent_8.1.3.140.zip \ -&& 7z x dm8_20240408_x86_rh7_64.iso \ -&& rm -f dm8_20240408_x86_rh7_64.iso \ -&& chmod +x DMInstall.bin +FROM base-dm \ No newline at end of file diff --git a/python/.devenv b/python/.devenv new file mode 100644 index 0000000..4139b95 --- /dev/null +++ b/python/.devenv @@ -0,0 +1,27 @@ +SHELL=/bin/bash +KUBERNETES_SERVICE_PORT_HTTPS=443 +KUBERNETES_SERVICE_PORT=443 +HOSTNAME=flask-app-deployment-76d66d98b8-s6hxs +DMCCA_CANVAS_NAME=flask-app +PWD=/home/flask +LOGNAME=flask +TZ=Asia/Shanghai +HOME=/home/flask +LANG=zh_CN.UTF-8 +KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 +VIRTUAL_ENV=/home/flask/venv +TERM=xterm +USER=flask +DMCCA_CANVAS_NAMESPACE=cnsof50011836-system +SHLVL=2 +KUBERNETES_PORT_443_TCP_PROTO=tcp +KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 +KUBERNETES_SERVICE_HOST=10.96.0.1 +LC_ALL=zh_CN.UTF-8 +KUBERNETES_PORT=tcp://10.96.0.1:443 +KUBERNETES_PORT_443_TCP_PORT=443 +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin +MAIL=/var/mail/flask +DEBIAN_FRONTEND=noninteractive +OLDPWD=/root +_=/usr/bin/printenv \ No newline at end of file diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..ebed232 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,3 @@ +.idea +__pycache__ +public \ No newline at end of file diff --git a/python/Dockerfile b/python/Dockerfile index 4d0eac3..db826f5 100644 --- a/python/Dockerfile +++ b/python/Dockerfile @@ -4,10 +4,12 @@ FROM base RUN pip install --upgrade pip # 创建一个用户运行flask -RUN adduser flask && \ +RUN useradd -m -s /bin/bash flask && \ chown -R flask:flask /home/flask && \ mkdir -p /var/log/flask-app && touch /var/log/flask-app/flask-app.err.log && touch /var/log/flask-app/flask-app.out.log && \ -chown -R flask:flask /var/log/flask-app +chown -R flask:flask /var/log/flask-app && \ +service ssh start && \ +service ssh restart WORKDIR /home/flask USER flask @@ -24,10 +26,12 @@ RUN python3 -m venv "$VIRTUAL_ENV" && \ export FLASK_APP=app.py && \ pip3 install dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && rm dmPython-2.5.5-cp310-cp310-linux_x86_64.whl && \ MAKEFLAGS="-j$(nproc)" pip install -r requirements.txt + COPY --from=build-dmpython /home/dmdba/build_artifacts/libdmdpi.so /usr/lib/ COPY --from=build-frontend /usr/src/app/build/ /home/flask/public/ # 暴露端口 -EXPOSE 5000 +EXPOSE 8000 22 8765 +USER root CMD ["gunicorn", "-w", "3", "-t", "60", "-b", "0.0.0.0:8000", "app:app"] \ No newline at end of file diff --git a/python/app.py b/python/app.py index 26960f9..bd443e9 100644 --- a/python/app.py +++ b/python/app.py @@ -1,12 +1,29 @@ +import subprocess +import threading +import time + +import paramiko + from teacher_func import * from student_func import * +from k8s_func import * + from flask import Flask, render_template, request, jsonify, send_from_directory, session from flask_cors import CORS - +from flask_socketio import SocketIO, emit, disconnect +import logging +logging.basicConfig(level=logging.DEBUG) app = Flask(__name__, static_folder="public") -CORS(app, resources={r"/*": {"origins": "*"}}) +CORS(app) +cors = CORS(app, resource={ + r"/*":{ + "origins":"*" + } +}) app.config['SECRET_KEY'] = '350625' +socketio = SocketIO(app, cors_allowed_origins="*") +clients = {} @app.route('/api/student', methods=['POST']) # 检测历史是否登录过,登录则直接进入index,反之则进去login界面 def student(): # 判断是否已经登录 @@ -249,6 +266,17 @@ def Find_details(): result=Find_details_Func(ID) print(result) return jsonify({'TestScore':result}) +@app.route("/api/teacher/list_pods") +def teacher_list_pods(): + return list_pods() +@app.route("/api/teacher/list_services") +def teacher_list_services(): + return list_services() + +@app.route("/api/teacher/create_pod") +def teacher_create_pod(): + create_pod(0, "test") + @app.route('/') @@ -257,7 +285,56 @@ def catch_all(path = "index.html"): return send_from_directory("public", path) +@socketio.on('connect_ssh') +def handle_connect_ssh(data): + ip = data['ip'] + port = int(data['port']) + password = data['password'] + client_id = request.sid + + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + ssh_client.connect(ip, port=port, username='your-username', password=password) + transport = ssh_client.get_transport() + channel = transport.open_session() + channel.get_pty() + channel.invoke_shell() + + clients[client_id] = { + 'ssh_client': ssh_client, + 'channel': channel + } + + socketio.start_background_task(target=read_from_channel, channel=channel, client_id=client_id) + except Exception as e: + emit('output', f'Connection failed: {str(e)}') + + +def read_from_channel(channel, client_id): + while True: + if channel.recv_ready(): + data = channel.recv(1024).decode('utf-8') + socketio.emit('output', data, room=client_id) + + +@socketio.on('input') +def handle_input(data): + client_id = request.sid + if client_id in clients: + clients[client_id]['channel'].send(data) + + +@socketio.on('disconnect') +def handle_disconnect(): + client_id = request.sid + if client_id in clients: + clients[client_id]['channel'].close() + clients[client_id]['ssh_client'].close() + del clients[client_id] if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) + socketio.run(app, host='0.0.0.0', port=5000) + diff --git a/python/assets/type0-pod.yaml b/python/assets/type0-pod.yaml new file mode 100644 index 0000000..667ef74 --- /dev/null +++ b/python/assets/type0-pod.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: NAME + namespace: "" + labels: + app: NAME +spec: + containers: + - name: base-dm + image: harbor.dameng.io/cnsof50011836/base-dm + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: '1' + memory: 1Gi + requests: + cpu: '0.25' + memory: 0.5G \ No newline at end of file diff --git a/python/k8s_func.py b/python/k8s_func.py new file mode 100644 index 0000000..311c040 --- /dev/null +++ b/python/k8s_func.py @@ -0,0 +1,85 @@ +import json + +import yaml +from kubernetes import client, config +from flask import jsonify +# Load in-cluster config +config.load_incluster_config() +v1 = client.CoreV1Api() +namespace = open("/var/run/secrets/kubernetes.io/serviceaccount/namespace").read() +IP = "36.138.114.105" +def list_pods(): + ret = v1.list_namespaced_pod(namespace, watch=False) + return { + "list": [{ + "name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"), + "ip": item.status.pod_ip + } for item in ret.items] + } + +def list_services(): + ret = v1.list_namespaced_service(namespace) + return { + "list": [ + # "name": item.metadata.name.replace("database-dmserver-0", "student-2210502337-0-12"), + # "ip": item.spec.cluster_ip, + # "ports": [[port.to_dict() for port in item.spec.ports]], + # + item.to_dict() + # "node_port": item.spec.ports[0].node_port, + for item in ret.items] + } + +def create_pod(type, name): + # 读取 pod yaml 文件 + with open(f"assets/type{type}-pod.yaml") as f: + pod_manifest = yaml.safe_load(f) + pod_manifest["metadata"]["name"] = name + pod_manifest["metadata"]["labels"]["app"] = name + # 创建 Pod + resp = v1.create_namespaced_pod( + body=pod_manifest, + namespace=namespace + ) + print(f"pod '{resp.metadata.name}' 创建成功") + # 创建 Service + service_manifest = { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": name+ "-service" + }, + "spec": { + "selector": { + "app": pod_manifest['metadata']['labels']['app'] + }, + "ports": [ + { + "protocol": "TCP", + "port": 22, + "targetPort": 22 + }, + { + "protocol": "TCP", + "port": 5236, + "targetPort": 5236 + } + ] + } + } + + resp = v1.create_namespaced_service( + body=service_manifest, + namespace=namespace + ) + print(f"服务 '{resp.metadata.name}' 创建成功") + # 创建成功,再次查询,拿到端口 + service = v1.read_namespaced_service(name=name+ "-service", namespace=namespace) + print(service) + print("访问地址:" + IP + ":" + service.spec.ports[0].node_port) + + +def delete_pod(name): + pass + + diff --git a/python/requirements.txt b/python/requirements.txt index 1eddad4..a785c55 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,4 +1,8 @@ Flask==2.3.0 Flask_Cors==4.0.0 pycryptodome==3.19.0 -gunicorn \ No newline at end of file +gunicorn +kubernetes +websockets +paramiko +flask-socketio \ No newline at end of file diff --git a/python/student_func.py b/python/student_func.py index beab01e..306b76d 100644 --- a/python/student_func.py +++ b/python/student_func.py @@ -117,7 +117,7 @@ def get_lesson_func(id): def student_succeed_func(ID): cursor = db.cursor() - cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}') + cursor.execute(f'SELECT * FROM STUDENT WHERE ID={ID}')# 报错 student_succeed = cursor.fetchall()[0] student_succeeds = student_succeed[0], student_succeed[1], student_succeed[3], student_succeed[4] cursor.close()