对webssh实现命令回放功能

想要实现webssh的命令记录功能需要一个前端的组件 asciinema

django服务端代码

 1 class WebSSH(WebsocketConsumer):
 2     message = {'status': 0, 'message': None}
 3     """
 4     status:
 5         0: ssh 连接正常, websocket 正常
 6         1: 发生未知错误, 关闭 ssh 和 websocket 连接
 7 
 8     message:
 9         status 为 1 时, message 为具体的错误信息
10         status 为 0 时, message 为 ssh 返回的数据, 前端页面将获取 ssh 返回的数据并写入终端页面
11     """
12 
13     def connect(self):
14         try:
15             self.accept()
16             logger.info('open connet service ssh')
17 
18             query_string = self.scope['query_string']
19             logger.info(query_string)
20             connet_argv = QueryDict(query_string=query_string, encoding='utf-8')
21             logger.info(connet_argv)
22             serverid = connet_argv.get('unique')
23             width = connet_argv.get('width')
24             height = connet_argv.get('height')
25             appid = connet_argv.get('appid','')
26             execuserid = connet_argv.get('execuserid','')
27 
28             width = int(width)
29             height = int(height)
30 
31             service = models.ServiceModel.objects.get(id=serverid)
32 
33             host = service.ipaddress
34             port = str(service.port)
35             user = system_config.log_user
36             logger.info('{} {} {} {} {}'.format(serverid,height,serverid,host,user))
37             self.ssh = SSH(websocker=self, message=self.message)
38             self.execuserid = execuserid
39             self.serverid = serverid
40 
41 
42             self.ssh.connect(
43                 host=host,
44                 user=user,
45                 port=port,
46                 pty_width=width,
47                 pty_height=height
48             )
49             if appid:
50                 app = models.ApplicationModel.objects.get(id=appid)
51                 self.ssh.shell('/home/{}/showlog.sh {}\n'.format(user,app.tomcatname))
52 
53         except Exception as e:
54             logger.info(e)
55             self.message['status'] = 1
56             self.message['message'] = str(e)
57             message = json.dumps(self.message)
58             self.send(message)
59             self.close()
60 
61     def disconnect(self, close_code):
62         try:
63             self.ssh.close()
64         except:
65             pass
66 
67     def receive(self, text_data=None, bytes_data=None):
68         data = json.loads(text_data)
69         if type(data) == dict:
70             status = data['status']
71             if status == 0:
72                 data = data['data']
73                 self.ssh.shell(data,self) #这里执行命令的时候把对象本身传进去(用了channels和xterm.js通过websocket实现的webssh是一个字符一个字符发送的,在parimako这个模块中做了一系列的命令组装,组成一个完整的shell命令)
74 
75             else:
76                 cols = data['cols']
77                 rows = data['rows']
78                 self.ssh.resize_pty(cols=cols, rows=rows)
79 
80     def savecommend(self,commend):   #这里是保存命令的方法
81         servicecommit = ServiceCommitModel()
82         servicecommit.service_id = self.serverid
83         servicecommit.user_id = self.execuserid
84         json_commend = json.dumps(commend)
85         servicecommit.commitname = json_commend[1:-1]
86         servicecommit.save()
87         logger.info(json_commend)

 

 1 import paramiko
 2 from threading import Thread
 3 from libs.ansible_libs.tools import get_key_obj
 4 import socket
 5 import json
 6 import logging
 7 
 8 
 9 class SSH:
10     def __init__(self, websocker, message):
11         self.websocker = websocker
12         self.message = message
13 
14     def connect(self, host, user, password=None, pkey=None, port=22, timeout=120,
15                 term='xterm', pty_width=80, pty_height=24):
16         try:
17             ssh_client = paramiko.SSHClient()
18             ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
19 
20             if password:
21                 ssh_client.connect(username=user, password=password, hostname=host, port=port, timeout=timeout)
22             else:
23                 ssh_client.connect(username=user, hostname=host, port=port, timeout=timeout)
24 
25             transport = ssh_client.get_transport()
26             self.channel = transport.open_session()
27             self.channel.get_pty(term=term, width=pty_width, height=pty_height)
28             self.channel.invoke_shell()
29 
30             for i in range(2):
31                 recv = self.channel.recv(1024).decode('utf-8')
32                 self.message['status'] = 0
33                 self.message['message'] = recv
34                 message = json.dumps(self.message)
35                 self.websocker.send(message)
36 
37         except socket.timeout as e:
38             self.message['status'] = 1
39             self.message['message'] = 'ssh 连接超时'
40             message = json.dumps(self.message)
41             logging.info('connet server timeout')
42             self.websocker.send(message)
43             self.websocker.close()
44         except Exception as e:
45             self.message['status'] = 1
46             self.message['message'] = str(e)
47             message = json.dumps(self.message)
48             logging.info('connet server custom')
49             self.websocker.send(message)
50             self.websocker.close()
51 
52     def resize_pty(self, cols, rows):
53         self.channel.resize_pty(width=cols, height=rows)
54 
55 
56     def django_to_ssh(self, data):
57         try:
58             self.channel.send(data)
59             return
60         except:
61             self.close()
62 
63     def websocket_to_django(self,httpobj):
64         try:
65             while True:
66                 data = self.channel.recv(1024).decode('utf-8')
67                 if not len(data):
68                     return
69                 self.message['status'] = 0
70                 self.message['message'] = data
71                 message = json.dumps(self.message)  
72                 self.websocker.send(message)
73                 print(data)
74                 try:
75                     if httpobj:
76                         httpobj.savecommend(str(data))  在这里做命令记录,把命令保存到数据库
77                 except Exception as e:
78                     print(e)
79                 # httpobj.savecommend(data)
80         except Exception as e:
81             print(e)
82             self.close()
83 
84     def close(self):
85         self.message['status'] = 1
86         self.message['message'] = '关闭连接'
87         message = json.dumps(self.message)
88         self.websocker.send(message)
89         self.channel.close()
90         self.websocker.close()
91 
92     def shell(self, data, httpobj=None):
93         Thread(target=self.django_to_ssh, args=(data,)).start()
94         Thread(target=self.websocket_to_django, args=(httpobj,)).start()  #这里是返回到前端的线程,在这个线程中做命令的记录

当需要查看命令回放的时候

 1 class CommandData(LoginRequiredMixin,View):
 2     def get(self,request):
 3         '''
 4         获取用户选择的参数以返回视频文件
 5         :param request:
 6         :return:
 7         '''
 8         serverid = request.GET.get('sid')
 9         end_time = request.GET.get('end_time')
10         start_time = request.GET.get('start_time')
11         userid = request.GET.get('uid')
12         servers = ServiceModel.objects.all()
13         users = User.objects.all()
14         datas = ServiceCommitModel.objects.filter(date__gte=start_time,date__lte=end_time,service_id=serverid,user_id=userid).values('commitname')
15         # fileurl = '/home/PyObject/static/image/demo/demo.json'
16         fileurl = '/opt/object/static/image/demo/demo.json'
17         datalist = list()
18         playindex = 0
19         with open(fileurl,'wb') as f:
20             f.write(bytes('{"version": 2, "width": 1500, "height": 1000, "timestamp": 1559530296, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}\n',encoding='utf-8')) #这里是asciinema需要的文件头
21             for data in datas:
22                 playindex += 0.4  #设置播放速度
23 
24                 commitname = data['commitname']
25 
26                 linestr = '[{}, "o", "{}"]\n'.format(playindex,commitname) #把命令写入到asciiname文件里面(必须按照这种格式)
27                 # print(type(commitname),print(commitname))
28                 f.write(bytes(linestr,encoding='utf-8')) 以二进制格式写入
29             # datalist.append(data['commitname'])
30         # print(json.dumps(datalist))
31         # return JsonResponse({
32         #     'datas': json.dumps(datalist)
33         # })
34         player = 'ok'
35         return render(request,'command/playbackcommend.html',locals()) 返回页面

前端网页:

在html中导入asciinema的js和css文件

<div class="row">
    {% if player %}
         <asciinema-player src="{% static 'image/demo/demo.json' %}" cols="260" rows="40"></asciinema-player>
    {% endif %}
</div>

这样就实现了webssh的命令回放功能

 

posted @ 2019-08-31 17:13  盈波秋水泛清涛  阅读(1236)  评论(13编辑  收藏  举报