From 0390a9003afecd6c183ddeddc312db3d8ab0d312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E4=BA=8C=E7=8C=9B?= Date: Thu, 12 Dec 2019 11:39:35 +0800 Subject: [PATCH] A api update --- spug_api/apps/consumer/consumers.py | 22 ------- spug_api/apps/host/urls.py | 5 +- spug_api/apps/host/views.py | 10 +++ spug_api/{apps => }/consumer/__init__.py | 0 spug_api/consumer/consumers.py | 77 +++++++++++++++++++++++ spug_api/{apps => }/consumer/executors.py | 0 spug_api/{apps => }/consumer/routing.py | 1 + spug_api/libs/ssh.py | 14 +++-- spug_api/spug/routing.py | 2 +- spug_api/spug/settings.py | 8 +++ spug_api/templates/web_ssh.html | 22 +++++++ 11 files changed, 131 insertions(+), 30 deletions(-) delete mode 100644 spug_api/apps/consumer/consumers.py rename spug_api/{apps => }/consumer/__init__.py (100%) create mode 100644 spug_api/consumer/consumers.py rename spug_api/{apps => }/consumer/executors.py (100%) rename spug_api/{apps => }/consumer/routing.py (70%) create mode 100644 spug_api/templates/web_ssh.html diff --git a/spug_api/apps/consumer/consumers.py b/spug_api/apps/consumer/consumers.py deleted file mode 100644 index 2bb042a..0000000 --- a/spug_api/apps/consumer/consumers.py +++ /dev/null @@ -1,22 +0,0 @@ -from channels.generic.websocket import WebsocketConsumer -from django_redis import get_redis_connection - - -class ExecConsumer(WebsocketConsumer): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.token = self.scope['url_route']['kwargs']['token'] - self.rds = get_redis_connection() - - def connect(self): - self.accept() - - def disconnect(self, code): - self.rds.close() - - def receive(self, **kwargs): - response = self.rds.blpop(self.token, timeout=5) - while response: - self.send(text_data=response[1].decode()) - response = self.rds.blpop(self.token, timeout=5) - self.send(text_data='pong') diff --git a/spug_api/apps/host/urls.py b/spug_api/apps/host/urls.py index 2741e52..0937203 100644 --- a/spug_api/apps/host/urls.py +++ b/spug_api/apps/host/urls.py @@ -1,7 +1,8 @@ -from django.conf.urls import url +from django.urls import path from .views import * urlpatterns = [ - url(r'^$', HostView.as_view()), + path('', HostView.as_view()), + path('ssh//', web_ssh), ] diff --git a/spug_api/apps/host/views.py b/spug_api/apps/host/views.py index 483b722..2e04a60 100644 --- a/spug_api/apps/host/views.py +++ b/spug_api/apps/host/views.py @@ -1,4 +1,6 @@ from django.views.generic import View +from django.shortcuts import render +from django.http.response import HttpResponseBadRequest from libs import json_response, JsonParser, Argument from apps.setting.utils import AppSetting from apps.host.models import Host @@ -46,6 +48,14 @@ class HostView(View): return json_response(error=error) +def web_ssh(request, h_id): + host = Host.objects.filter(pk=h_id).first() + if not host: + return HttpResponseBadRequest('unknown host') + context = {'id': h_id, 'title': host.name, 'token': request.user.access_token} + return render(request, 'web_ssh.html', context) + + def valid_ssh(hostname, port, username, password): try: private_key = AppSetting.get('private_key') diff --git a/spug_api/apps/consumer/__init__.py b/spug_api/consumer/__init__.py similarity index 100% rename from spug_api/apps/consumer/__init__.py rename to spug_api/consumer/__init__.py diff --git a/spug_api/consumer/consumers.py b/spug_api/consumer/consumers.py new file mode 100644 index 0000000..1ab5c96 --- /dev/null +++ b/spug_api/consumer/consumers.py @@ -0,0 +1,77 @@ +from channels.generic.websocket import WebsocketConsumer +from django_redis import get_redis_connection +from apps.setting.utils import AppSetting +from apps.host.models import Host +from threading import Thread +import json + + +class ExecConsumer(WebsocketConsumer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.token = self.scope['url_route']['kwargs']['token'] + self.rds = get_redis_connection() + + def connect(self): + self.accept() + + def disconnect(self, code): + self.rds.close() + + def receive(self, **kwargs): + response = self.rds.blpop(self.token, timeout=5) + while response: + self.send(text_data=response[1].decode()) + response = self.rds.blpop(self.token, timeout=5) + self.send(text_data='pong') + + +class SSHConsumer(WebsocketConsumer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + kwargs = self.scope['url_route']['kwargs'] + self.token = kwargs['token'] + self.id = kwargs['id'] + self.chan = None + self.ssh = None + + def loop_read(self): + while True: + data = self.chan.recv(32 * 1024) + # print('read: {!r}'.format(data)) + if not data: + self.close(3333) + break + self.send(bytes_data=data) + + def receive(self, text_data=None, bytes_data=None): + data = text_data or bytes_data + if data: + data = json.loads(data) + # print('write: {!r}'.format(data)) + resize = data.get('resize') + if resize and len(resize) == 2: + self.chan.resize_pty(*resize) + else: + self.chan.send(data['data']) + + def disconnect(self, code): + self.chan.close() + self.ssh.close() + # print('Connection close') + + def connect(self): + self.accept() + self.send(bytes_data=b'Connecting ...\r\n') + host = Host.objects.filter(pk=self.id).first() + if not host: + self.send(text_data='Unknown host\r\n') + self.close() + try: + self.ssh = host.get_ssh(AppSetting.get('private_key')).get_client() + except Exception as e: + self.send(bytes_data=f'Exception: {e}\r\n'.encode()) + self.close() + self.chan = self.ssh.invoke_shell(term='xterm') + self.chan.transport.set_keepalive(30) + Thread(target=self.loop_read).start() diff --git a/spug_api/apps/consumer/executors.py b/spug_api/consumer/executors.py similarity index 100% rename from spug_api/apps/consumer/executors.py rename to spug_api/consumer/executors.py diff --git a/spug_api/apps/consumer/routing.py b/spug_api/consumer/routing.py similarity index 70% rename from spug_api/apps/consumer/routing.py rename to spug_api/consumer/routing.py index f58379d..4160413 100644 --- a/spug_api/apps/consumer/routing.py +++ b/spug_api/consumer/routing.py @@ -3,4 +3,5 @@ from .consumers import * websocket_urlpatterns = [ path('ws/exec//', ExecConsumer), + path('ws/ssh///', SSHConsumer), ] diff --git a/spug_api/libs/ssh.py b/spug_api/libs/ssh.py index 9a8d3d8..d9e6bd4 100644 --- a/spug_api/libs/ssh.py +++ b/spug_api/libs/ssh.py @@ -38,6 +38,14 @@ class SSH: with self: return True + def get_client(self): + if self.client is not None: + return self.client + self.client = SSHClient() + self.client.set_missing_host_key_policy(AutoAddPolicy) + self.client.connect(**self.arguments) + return self.client + def exec_command(self, command, timeout=1800, environment=None): with self as cli: chan = cli.get_transport().open_session() @@ -67,11 +75,7 @@ class SSH: def __enter__(self): if self.client is not None: raise RuntimeError('Already connected') - client = SSHClient() - client.set_missing_host_key_policy(AutoAddPolicy) - client.connect(**self.arguments) - self.client = client - return self.client + return self.get_client() def __exit__(self, exc_type, exc_val, exc_tb): self.client.close() diff --git a/spug_api/spug/routing.py b/spug_api/spug/routing.py index 0dcac66..770c08b 100644 --- a/spug_api/spug/routing.py +++ b/spug_api/spug/routing.py @@ -1,5 +1,5 @@ from channels.routing import ProtocolTypeRouter, ChannelNameRouter, URLRouter -from apps.consumer import routing, executors +from consumer import routing, executors application = ProtocolTypeRouter({ 'channel': ChannelNameRouter({ diff --git a/spug_api/spug/settings.py b/spug_api/spug/settings.py index 3328721..55c70d8 100644 --- a/spug_api/spug/settings.py +++ b/spug_api/spug/settings.py @@ -82,6 +82,14 @@ CHANNEL_LAYERS = { }, } +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')], + 'APP_DIRS': False, + }, +] + SCHEDULE_KEY = 'spug:schedule' MONITOR_KEY = 'spug:monitor' diff --git a/spug_api/templates/web_ssh.html b/spug_api/templates/web_ssh.html new file mode 100644 index 0000000..6b8e2db --- /dev/null +++ b/spug_api/templates/web_ssh.html @@ -0,0 +1,22 @@ + + + + + {{ title }} + + + + +
+ + + + + + \ No newline at end of file