优化connect.py排序

pull/26/head
ibuler 2015-08-22 00:01:54 +08:00
parent ad16d8b532
commit c1facb939e
3 changed files with 199 additions and 205 deletions

View File

@ -5,40 +5,22 @@ import sys
reload(sys)
sys.setdefaultencoding('utf8')
import socket
import os
import re
import select
import time
import paramiko
import struct
import fcntl
import signal
import textwrap
import getpass
import fnmatch
import readline
import django
import datetime
from multiprocessing import Pool
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
if django.get_version() != '1.6':
django.setup()
from jlog.models import Log
from jumpserver.api import CONF, BASE_DIR, ServerError, User, UserGroup, Asset, get_object
from jumpserver.api import BASE_DIR, ServerError, User, UserGroup, Asset, Jtty, get_object
from jumpserver.api import CRYPTOR, logger, is_dir
from jumpserver.api import BisGroup as AssetGroup
try:
import termios
import tty
except ImportError:
print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m'
time.sleep(3)
sys.exit()
log_dir = os.path.join(BASE_DIR, 'logs')
login_user = get_object(User, username=getpass.getuser())
@ -57,184 +39,6 @@ def color_print(msg, color='red', exits=False):
sys.exit()
class Jtty(object):
def __init__(self, user, asset):
self.chan = None
self.username = user.username
self.ip = asset.ip
self.user = user
self.asset = asset
@staticmethod
def get_win_size():
"""
This function use to get the size of the windows!
获得terminal窗口大小
"""
if 'TIOCGWINSZ' in dir(termios):
TIOCGWINSZ = termios.TIOCGWINSZ
else:
TIOCGWINSZ = 1074295912L
s = struct.pack('HHHH', 0, 0, 0, 0)
x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
return struct.unpack('HHHH', x)[0:2]
def set_win_size(self, sig, data):
"""
This function use to set the window size of the terminal!
设置terminal窗口大小
"""
try:
win_size = self.get_win_size()
self.chan.resize_pty(height=win_size[0], width=win_size[1])
except Exception:
pass
def log_record(self):
"""
Logging user command and output.
记录用户的日志
"""
tty_log_dir = os.path.join(log_dir, 'tty')
timestamp_start = int(time.time())
date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start))
time_start = time.strftime('%H%M%S', time.localtime(timestamp_start))
log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start)
today_connect_log_dir = os.path.join(tty_log_dir, date_start)
log_file_path = os.path.join(today_connect_log_dir, log_filename)
dept_name = self.user.dept.name
pid = os.getpid()
pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip()
ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n')
try:
is_dir(today_connect_log_dir)
except OSError:
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
try:
log_file = open(log_file_path, 'a')
except IOError:
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name,
log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid)
log_file.write('Start time is %s\n' % datetime.datetime.now())
log.save()
return log_file, log
def posix_shell(self):
"""
Use paramiko channel connect server interactive.
使用paramiko模块的channel连接后端进入交互式
"""
log_file, log = self.log_record()
old_tty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
self.chan.settimeout(0.0)
while True:
try:
r, w, e = select.select([self.chan, sys.stdin], [], [])
except Exception:
pass
if self.chan in r:
try:
x = self.chan.recv(1024)
if len(x) == 0:
break
sys.stdout.write(x)
sys.stdout.flush()
log_file.write(x)
log_file.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = os.read(sys.stdin.fileno(), 1)
if len(x) == 0:
break
self.chan.send(x)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
log_file.write('End time is %s' % datetime.datetime.now())
log_file.close()
log.is_finished = True
log.handle_finished = False
log.end_time = datetime.datetime.now()
log.save()
def get_connect_item(self):
"""获取连接需要的参数也就是服务ip, 端口, 用户账号和密码"""
if not self.asset.is_active:
raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
if not self.user.is_active:
raise ServerError('该用户被禁用 User %s is not active.' % self.username)
login_type_dict = {
'L': self.user.ldap_pwd,
}
if self.asset.login_type in login_type_dict:
password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type])
return self.username, password, self.ip, int(self.asset.port)
elif self.asset.login_type == 'M':
username = self.asset.username
password = CRYPTOR.decrypt(self.asset.password)
return username, password, self.ip, int(self.asset.port)
else:
raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]')
def connect(self):
"""
Connect server.
连接服务器
"""
username, password, ip, port = self.get_connect_item()
logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port))
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip
# 发起ssh连接请求 Make a ssh connection
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(ip, port=port, username=username, password=password, compress=True)
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
raise ServerError('认证错误 Authentication Error.')
except socket.error:
raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.')
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
global channel
win_size = self.get_win_size()
self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1])
try:
signal.signal(signal.SIGWINCH, self.set_win_size)
except:
pass
# 设置PS1并提示 Set PS1 and msg it
channel.send(ps1)
channel.send(login_msg)
# Make ssh interactive tunnel
self.posix_shell()
# Shutdown channel socket
channel.close()
ssh.close()
def verify_connect(user, option):
"""鉴定用户是否有该主机权限 或 匹配到的ip是否唯一"""
ip_matched = []

View File

@ -28,11 +28,15 @@ class BisGroup(models.Model):
def get_asset_info(self, printable=False):
assets = self.get_asset()
ip_comment = {}
for asset in assets:
if asset.comment:
print '%-15s -- %s' % (asset.ip, asset.comment)
ip_comment[asset.ip] = asset.comment
for ip in sorted(ip_comment):
if ip_comment[ip]:
print '%-15s -- %s' % (ip, ip_comment[ip])
else:
print '%-15s' % asset.ip
print '%-15s' % ip
print ''
def get_asset_num(self):

View File

@ -1,9 +1,6 @@
# coding: utf-8
from django.http import HttpResponseRedirect
import json
import os
import os, sys, time
from ConfigParser import ConfigParser
import getpass
from Crypto.Cipher import AES
@ -14,6 +11,8 @@ import hashlib
import datetime
import random
import subprocess
import paramiko
import struct, fcntl, signal,socket, select, fnmatch
from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response
@ -22,10 +21,19 @@ from jasset.models import Asset, BisGroup, IDC
from jlog.models import Log
from jasset.models import AssetAlias
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponseRedirect
from django.core.mail import send_mail
import json
import logging
try:
import termios
import tty
except ImportError:
print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m'
time.sleep(3)
sys.exit()
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
CONF = ConfigParser()
@ -40,7 +48,7 @@ LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable')
SEND_IP = CONF.get('base', 'ip')
SEND_PORT = CONF.get('base', 'port')
MAIL_FROM = CONF.get('mail', 'email_host_user')
log_dir = os.path.join(BASE_DIR, 'logs')
def set_log(level):
log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR,
@ -139,6 +147,184 @@ def pages(posts, r):
return contact_list, p, contacts, page_range, current_page, show_first, show_end
class Jtty(object):
def __init__(self, user, asset):
self.chan = None
self.username = user.username
self.ip = asset.ip
self.user = user
self.asset = asset
@staticmethod
def get_win_size():
"""
This function use to get the size of the windows!
获得terminal窗口大小
"""
if 'TIOCGWINSZ' in dir(termios):
TIOCGWINSZ = termios.TIOCGWINSZ
else:
TIOCGWINSZ = 1074295912L
s = struct.pack('HHHH', 0, 0, 0, 0)
x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
return struct.unpack('HHHH', x)[0:2]
def set_win_size(self, sig, data):
"""
This function use to set the window size of the terminal!
设置terminal窗口大小
"""
try:
win_size = self.get_win_size()
self.chan.resize_pty(height=win_size[0], width=win_size[1])
except Exception:
pass
def log_record(self):
"""
Logging user command and output.
记录用户的日志
"""
tty_log_dir = os.path.join(log_dir, 'tty')
timestamp_start = int(time.time())
date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start))
time_start = time.strftime('%H%M%S', time.localtime(timestamp_start))
log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start)
today_connect_log_dir = os.path.join(tty_log_dir, date_start)
log_file_path = os.path.join(today_connect_log_dir, log_filename)
dept_name = self.user.dept.name
pid = os.getpid()
pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip()
ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n')
try:
is_dir(today_connect_log_dir)
except OSError:
raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir))
try:
log_file = open(log_file_path, 'a')
except IOError:
raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir)
log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name,
log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid)
log_file.write('Start time is %s\n' % datetime.datetime.now())
log.save()
return log_file, log
def posix_shell(self):
"""
Use paramiko channel connect server interactive.
使用paramiko模块的channel连接后端进入交互式
"""
log_file, log = self.log_record()
old_tty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
self.chan.settimeout(0.0)
while True:
try:
r, w, e = select.select([self.chan, sys.stdin], [], [])
except Exception:
pass
if self.chan in r:
try:
x = self.chan.recv(1024)
if len(x) == 0:
break
sys.stdout.write(x)
sys.stdout.flush()
log_file.write(x)
log_file.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = os.read(sys.stdin.fileno(), 1)
if len(x) == 0:
break
self.chan.send(x)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
log_file.write('End time is %s' % datetime.datetime.now())
log_file.close()
log.is_finished = True
log.handle_finished = False
log.end_time = datetime.datetime.now()
log.save()
def get_connect_item(self):
"""获取连接需要的参数也就是服务ip, 端口, 用户账号和密码"""
if not self.asset.is_active:
raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
if not self.user.is_active:
raise ServerError('该用户被禁用 User %s is not active.' % self.username)
login_type_dict = {
'L': self.user.ldap_pwd,
}
if self.asset.login_type in login_type_dict:
password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type])
return self.username, password, self.ip, int(self.asset.port)
elif self.asset.login_type == 'M':
username = self.asset.username
password = CRYPTOR.decrypt(self.asset.password)
return username, password, self.ip, int(self.asset.port)
else:
raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]')
def connect(self):
"""
Connect server.
连接服务器
"""
username, password, ip, port = self.get_connect_item()
logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port))
ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip
login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip
# 发起ssh连接请求 Make a ssh connection
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(ip, port=port, username=username, password=password, compress=True)
except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException:
raise ServerError('认证错误 Authentication Error.')
except socket.error:
raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.')
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
global channel
win_size = self.get_win_size()
self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1])
try:
signal.signal(signal.SIGWINCH, self.set_win_size)
except:
pass
# 设置PS1并提示 Set PS1 and msg it
channel.send(ps1)
channel.send(login_msg)
# Make ssh interactive tunnel
self.posix_shell()
# Shutdown channel socket
channel.close()
ssh.close()
class PyCrypt(object):
"""
This class used to encrypt and decrypt password.