parent
8d5732df9d
commit
8ed2e800f6
|
@ -30,9 +30,9 @@ class HostModel(BaseModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'主机:{self.hostname}'
|
return f'主机:{self.hostname}'
|
||||||
|
|
||||||
def get_host_client(self, pkey=None, default_env=None):
|
def get_host_client(self, pkey=None):
|
||||||
pkey = pkey or self.private_key
|
pkey = pkey or self.private_key
|
||||||
return SSH(hostname=self.ip, port=self.port, username=self.username, pkey=pkey, default_env=default_env)
|
return SSH(hostname=self.ip, port=self.port, username=self.username, pkey=pkey)
|
||||||
|
|
||||||
|
|
||||||
class HostType(BaseModel):
|
class HostType(BaseModel):
|
||||||
|
|
|
@ -20,7 +20,6 @@ class BaseConstant:
|
||||||
SERVER_IP = get_ip_address()
|
SERVER_IP = get_ip_address()
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
DEFAULT_CACHE_REDIS = "redis://127.0.0.1:6379/1"
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
||||||
|
|
|
@ -4,7 +4,6 @@ from threading import Thread
|
||||||
|
|
||||||
from channels.generic.websocket import WebsocketConsumer
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
from channels.exceptions import StopConsumer
|
from channels.exceptions import StopConsumer
|
||||||
from django.contrib.auth.models import AnonymousUser
|
|
||||||
from django_redis import get_redis_connection
|
from django_redis import get_redis_connection
|
||||||
|
|
||||||
from apps.host.models import HostModel
|
from apps.host.models import HostModel
|
||||||
|
@ -30,7 +29,7 @@ class SshConsumer(WebsocketConsumer):
|
||||||
self.user = self.scope['user']
|
self.user = self.scope['user']
|
||||||
self.host_id = self.scope['url_route']['kwargs']['id']
|
self.host_id = self.scope['url_route']['kwargs']['id']
|
||||||
|
|
||||||
if isinstance(self.user, AnonymousUser):
|
if not self.user:
|
||||||
self.close()
|
self.close()
|
||||||
else:
|
else:
|
||||||
self.accept()
|
self.accept()
|
||||||
|
@ -65,12 +64,7 @@ class SshConsumer(WebsocketConsumer):
|
||||||
def receive(self, text_data=None, bytes_data=None):
|
def receive(self, text_data=None, bytes_data=None):
|
||||||
data = text_data or bytes_data
|
data = text_data or bytes_data
|
||||||
if data:
|
if data:
|
||||||
data = json.loads(data)
|
self.xterm.send(data)
|
||||||
resize = data.get('resize')
|
|
||||||
if resize and len(resize) == 2:
|
|
||||||
self.xterm.resize_pty(*resize)
|
|
||||||
else:
|
|
||||||
self.xterm.send(data['data'])
|
|
||||||
|
|
||||||
def websocket_disconnect(self, message):
|
def websocket_disconnect(self, message):
|
||||||
raise StopConsumer()
|
raise StopConsumer()
|
|
@ -43,8 +43,7 @@ class SshJob:
|
||||||
service_post_name = service_name + '_post'
|
service_post_name = service_name + '_post'
|
||||||
service_post_path = os.path.join(SCRIPTS_DIR, service_post_name)
|
service_post_path = os.path.join(SCRIPTS_DIR, service_post_name)
|
||||||
if os.path.exists(service_post_path):
|
if os.path.exists(service_post_path):
|
||||||
print(type(result))
|
command = "%s '%s'" % (service_post_path, result)
|
||||||
command = "%s '%s'" % (service_post_path, json.dumps(result))
|
|
||||||
output = os.popen(command)
|
output = os.popen(command)
|
||||||
result = ast.literal_eval(output.read())
|
result = ast.literal_eval(output.read())
|
||||||
update_job(instance=self.job, status="Success", result=result, host_by=host_ips)
|
update_job(instance=self.job, status="Success", result=result, host_by=host_ips)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from channels.db import database_sync_to_async
|
from channels.db import database_sync_to_async
|
||||||
from django.contrib.auth.models import AnonymousUser
|
|
||||||
|
|
||||||
from apps.accounts.models import User
|
from apps.accounts.models import User
|
||||||
from rest_framework_jwt.settings import api_settings
|
from rest_framework_jwt.settings import api_settings
|
||||||
|
@ -17,7 +16,7 @@ def get_user(user_id: int):
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return AnonymousUser()
|
return None
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,11 +33,11 @@ class AuthMiddleware:
|
||||||
user_id = None
|
user_id = None
|
||||||
token = scope.get('query_string', None)
|
token = scope.get('query_string', None)
|
||||||
if not token:
|
if not token:
|
||||||
scope['user'] = AnonymousUser()
|
scope['user'] = None
|
||||||
if token.decode().startswith('user_id='):
|
if token.decode().startswith('user_id='):
|
||||||
user_id = token.decode().replace('user_id=', '')
|
user_id = token.decode().replace('user_id=', '')
|
||||||
if not user_id:
|
if not user_id:
|
||||||
scope['user'] = AnonymousUser()
|
scope['user'] = None
|
||||||
else:
|
else:
|
||||||
scope['user'] = await get_user(user_id=user_id)
|
scope['user'] = await get_user(user_id=user_id)
|
||||||
return await self.application(scope, receive, send, *args, **kwargs)
|
return await self.application(scope, receive, send, *args, **kwargs)
|
||||||
|
|
|
@ -5,7 +5,6 @@ from .middleware import AuthMiddleware
|
||||||
|
|
||||||
ws_router = AuthMiddleware(
|
ws_router = AuthMiddleware(
|
||||||
URLRouter([
|
URLRouter([
|
||||||
path('ws/exec/<str:token>/', ExecConsumer.as_asgi()),
|
|
||||||
path('ws/ssh/<int:id>/', SshConsumer.as_asgi()),
|
path('ws/ssh/<int:id>/', SshConsumer.as_asgi()),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,9 +8,19 @@ https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from django.urls import path
|
||||||
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
from django.core.asgi import get_asgi_application
|
from django.core.asgi import get_asgi_application
|
||||||
|
from consumer.consumers import SshConsumer
|
||||||
|
from consumer.middleware import AuthMiddleware
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sysom.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sysom.settings')
|
||||||
|
|
||||||
application = get_asgi_application()
|
application = ProtocolTypeRouter({
|
||||||
|
"http": get_asgi_application(),
|
||||||
|
"websocket": AuthMiddleware(
|
||||||
|
URLRouter([
|
||||||
|
path('ws/ssh/<int:id>/', SshConsumer.as_asgi()),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
@ -48,17 +48,6 @@ DEBUG = constant.DEBUG
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
CORS_ORIGIN_ALLOW_ALL = True
|
CORS_ORIGIN_ALLOW_ALL = True
|
||||||
|
|
||||||
# Redis缓存
|
|
||||||
CACHES = {
|
|
||||||
"default": {
|
|
||||||
"BACKEND": "django_redis.cache.RedisCache",
|
|
||||||
"LOCATION": constant.DEFAULT_CACHE_REDIS,
|
|
||||||
"OPTIONS": {
|
|
||||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Mysql数据库
|
# Mysql数据库
|
||||||
DATABASES = constant.DATABASES
|
DATABASES = constant.DATABASES
|
||||||
|
|
||||||
|
@ -75,16 +64,6 @@ TEMPLATES = [
|
||||||
WSGI_APPLICATION = 'sysom.wsgi.application'
|
WSGI_APPLICATION = 'sysom.wsgi.application'
|
||||||
ASGI_APPLICATION = 'sysom.asgi.application'
|
ASGI_APPLICATION = 'sysom.asgi.application'
|
||||||
|
|
||||||
# channels 配置
|
|
||||||
CHANNEL_LAYERS = {
|
|
||||||
"default": {
|
|
||||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
|
||||||
"CONFIG": {
|
|
||||||
"hosts": [("127.0.0.1", 6379)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
LANGUAGE_CODE = constant.LANGUAGE_CODE
|
LANGUAGE_CODE = constant.LANGUAGE_CODE
|
||||||
|
|
|
@ -74,6 +74,11 @@ export default [
|
||||||
name: 'match',
|
name: 'match',
|
||||||
component: './vmcore/match',
|
component: './vmcore/match',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/vmcore/analyse',
|
||||||
|
name: 'analyse',
|
||||||
|
component: './vmcore/analyse',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,7 +67,11 @@
|
||||||
"react-dom": "^17.0.0",
|
"react-dom": "^17.0.0",
|
||||||
"react-helmet-async": "^1.0.4",
|
"react-helmet-async": "^1.0.4",
|
||||||
"umi": "^3.5.0",
|
"umi": "^3.5.0",
|
||||||
"umi-serve": "^1.9.10"
|
"umi-serve": "^1.9.10",
|
||||||
|
"xterm": "^4.16.0",
|
||||||
|
"xterm-addon-attach": "^0.6.0",
|
||||||
|
"xterm-addon-fit": "^0.5.0",
|
||||||
|
"xterm-addon-web-links": "^0.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/pro-cli": "^2.0.2",
|
"@ant-design/pro-cli": "^2.0.2",
|
||||||
|
|
|
@ -63,4 +63,5 @@ export default {
|
||||||
'menu.diagnose': '诊断中心',
|
'menu.diagnose': '诊断中心',
|
||||||
'menu.diagnose.io': 'IO延时诊断',
|
'menu.diagnose.io': 'IO延时诊断',
|
||||||
'menu.diagnose.net': '网络诊断',
|
'menu.diagnose.net': '网络诊断',
|
||||||
|
'menu.vmcore.analyse': '在线分析',
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import 'xterm/css/xterm.css';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
import { AttachAddon } from 'xterm-addon-attach';
|
||||||
|
|
||||||
|
const WebConsole = (props) => {
|
||||||
|
const divRef = useRef(null);
|
||||||
|
let socket = null;
|
||||||
|
|
||||||
|
const initTerminal = () => {
|
||||||
|
const terminal = new Terminal({
|
||||||
|
cursorBlink: true,
|
||||||
|
});
|
||||||
|
socket = new WebSocket(`ws://127.0.0.1:8001/ws/ssh/2/?user_id=1`);
|
||||||
|
socket.onopen = () => {
|
||||||
|
terminal.focus();
|
||||||
|
};
|
||||||
|
socket.onerror = () => {
|
||||||
|
message.error('连接出错')
|
||||||
|
};
|
||||||
|
const webLinksAddon = new WebLinksAddon();
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
const attachAddon = new AttachAddon(socket);
|
||||||
|
terminal.loadAddon(webLinksAddon);
|
||||||
|
terminal.loadAddon(fitAddon);
|
||||||
|
terminal.loadAddon(attachAddon);
|
||||||
|
terminal.open(divRef.current);
|
||||||
|
fitAddon.fit();
|
||||||
|
terminal.prompt = () => {
|
||||||
|
terminal.write('\r\n ');
|
||||||
|
};
|
||||||
|
terminal.prompt();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (socket) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
initTerminal();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<div style={{ marginTop: 10, width: 760, height: 500 }} ref={divRef} />;
|
||||||
|
</PageContainer>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WebConsole
|
|
@ -17546,6 +17546,26 @@ xtend@^4.0.0:
|
||||||
resolved "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
integrity sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=
|
integrity sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=
|
||||||
|
|
||||||
|
xterm-addon-attach@^0.6.0:
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.npmmirror.com/xterm-addon-attach/download/xterm-addon-attach-0.6.0.tgz#220c23addd62ab88c9914e2d4c06f7407e44680e"
|
||||||
|
integrity sha1-Igwjrd1iq4jJkU4tTAb3QH5EaA4=
|
||||||
|
|
||||||
|
xterm-addon-fit@^0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.npmmirror.com/xterm-addon-fit/download/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596"
|
||||||
|
integrity sha1-LVG5g7eGqX3NbN6AXnAMf5E7xZY=
|
||||||
|
|
||||||
|
xterm-addon-web-links@^0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.npmmirror.com/xterm-addon-web-links/download/xterm-addon-web-links-0.5.0.tgz#c6869c0032e6709e2437315199d794a3b94a183e"
|
||||||
|
integrity sha512-egDNFDvuArQ0QKE/ZdP1HMo70Lm8nSOjuIJY0kYjqCCE6yntjyuGzjeoYD+CIQ12gJ6B6thVh8nTApi27qYiag==
|
||||||
|
|
||||||
|
xterm@^4.16.0:
|
||||||
|
version "4.16.0"
|
||||||
|
resolved "https://registry.npmmirror.com/xterm/download/xterm-4.16.0.tgz#af25223c72917438842121e1bcd1b60ffd7e8476"
|
||||||
|
integrity sha512-nAbuigL9CYkI075mdfqpnB8cHZNKxENCj1CQ9Tm5gSvWkMtkanmRN2mkHGjSaET1/3+X9BqISFFo7Pd2mXVjiQ==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.npm.taobao.org/y18n/download/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
resolved "https://registry.npm.taobao.org/y18n/download/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||||
|
|
Loading…
Reference in New Issue