SQLALchemy(连表)、paramiko
本节内容:
一:外键存在的意义:
任何的数据都可以在一个表中存储,但是这样存储有这个问题:如果一个字段在一个表里多次出现,而且这个字段的长度比较大,那么将会在存储上有浪费。
这个时间如果出现另一张表,存储他们之间的关系,是不是更好呢?
但是如果这样做,还会出现一个问题,比如:A B 2张表中,A中存储的时候数字ID 和B表中的ID对应相应的字段,如果这个时候在插入B表不存在的ID,这样我们
就会造成一个问题:我们不清楚这个A表中这个ID 代表什么?诸如此类的问题:最后引入外键。
外键保证了A表中所有的对应类型的ID 都是B表中的存在的数字ID 也就是唯一性约束。如果B 关系表中不存在的ID,在A表插入的时候,会插入失败,并报错。
1)外键是mysql一种特殊的索引。创建了2个表的关系对应。
2)建立了唯一性约束。
问题:这几天测试外键的约束性,一直不成功,最后找到原因。因为使用的mysql的版本很低,默认的存储引擎是MyISAM。
1 mysql> show engines; 2 +------------+---------+------------------------------------------------------------+--------------+------+------------+ 3 | Engine | Support | Comment | Transactions | XA | Savepoints | 4 +------------+---------+------------------------------------------------------------+--------------+------+------------+ 5 | MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | 6 | CSV | YES | CSV storage engine | NO | NO | NO | 7 | MyISAM | YES | Default engine as of MySQL 3.23 with great performance | NO | NO | NO | 8 | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | 9 | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | 10 +------------+---------+------------------------------------------------------------+--------------+------+------------+ 11 5 rows in set (0.00 sec)
1 mysql> select @@version; 2 +-----------+ 3 | @@version | 4 +-----------+ 5 | 5.1.73 | 6 +-----------+ 7 1 row in set (0.01 sec)
引擎:MyISAM不支持外键约束。所以修改数据默认引擎。直接修改配置文件。在mysql配置文件(linux下为/etc/my.cnf),在mysqld后面增加default-storage-engine=INNODB即可。
重启mysql既可。
然后创建外键就有外键约束了。坑!!!!
二:SQLALchemy
注意SQLALchemy是通过类和对象创建创建相应的表结构。插入的数据也类的对象。
1 class User(Base): 2 __tablename__="user"#这个是创建的表的名字。 3 nid=Column(Integer,primary_key=True,autoincrement=True) 4 name=Column(String(12)) 5 group_id=Column(Integer,ForeignKey("group.group_id"))#注意ForeignKey是类 初始化对象。而不是等于。注意创建的外键里添加的字符串是表格名字不是类的名字!!! 6 7 class Group(Base): 8 __tablename__="group" 9 group_id=Column(Integer,primary_key=True) 10 name=Column(String(12))
上面的代码有问题:如果我们想设置主键的话,最好不要设置我们自己的值。最好设置单独一列做为主键要不然插值的时候报错。
sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, "Duplicate entry '1' for key 'PRIMARY'") [SQL: 'INSERT INTO group_1 (group_id, name) VALUES
一:单表查询
进行单表查询的时候,查询的结果返回的是类的一个对象:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy import Column, Integer, String, ForeignKey 3 from sqlalchemy.orm import sessionmaker,relationship 4 from sqlalchemy import create_engine 5 6 engine = create_engine("mysql+pymysql://root:@192.168.1.104:3306/day13", max_overflow=5) 7 Base = declarative_base() 8 class User(Base): 9 __tablename__ = 'user' 10 nid = Column(Integer, primary_key=True,autoincrement=True) 11 username = Column(String(32)) 12 group_id = Column(Integer,ForeignKey('cc.nid')) 13 Session = sessionmaker(bind=engine) 14 session = Session() 15 ret=session.query(User).filter(User.username=="alex1").all() 16 print(ret) 17 [<__main__.User object at 0x0344B390>]
对对象进行相应的操作:
1 ret=session.query(User).filter(User.username=="alex1").all() 2 print(ret[0].username) 3 alex1
根据之前学习的,当我们print输出一个对象默认是调用该对象的一个__str__方法。但是在SQLALchemy里 规定 调用的是__repr__方法,返回值是什么,在打印对象的时候就输出什么。
我们可以自定义__repr__方法来,重定向我们输出的结果,方便我们在操作表的时候,进行输出。
1 class User(Base): 2 __tablename__ = 'user' 3 nid = Column(Integer, primary_key=True,autoincrement=True) 4 username = Column(String(32)) 5 group_id = Column(Integer,ForeignKey('cc.nid')) 6 gruop=relationship("Group",backref="cc") 7 def __repr__(self): 8 result=('%s-%s')%(self.username,self.group_id) 9 return result 10 ret=session.query(User).filter(User.username=="alex1").all() 11 print(ret) 12 [alex1-1]
二:一对多,多表查询:
表结构:
如果进行多表查询的时候,原生sql如下:
1 mysql> select * from cc join user on user.group_id=cc.nid; 2 +-----+---------+-----+----------+----------+ 3 | nid | caption | nid | username | group_id | 4 +-----+---------+-----+----------+----------+ 5 | 1 | dba | 1 | alex1 | 1 | 6 +-----+---------+-----+----------+----------+ 7 1 row in set (0.00 sec)
在sqlalchemy里默认帮你把on后面的操作进行了。
1 ret=session.query(Group).join(User) 2 print(ret) 3 SELECT cc.nid AS cc_nid, cc.caption AS cc_caption FROM cc JOIN "user" ON cc.nid = "user".group_id
1 class Group(Base): 2 __tablename__ = 'cc' 3 nid = Column(Integer, primary_key=True,autoincrement=True) 4 caption = Column(String(32)) 5 def __repr__(self): 6 result=('%s-%s')%(self.nid,self.caption) 7 return result 8 9 ret=session.query(Group).join(User).all() 10 print(ret) 11 [1-dba]
如上是inner joner,在sqlalchemy里没有right join只有left join
1 mysql> select * from cc left join user on user.group_id=cc.nid; 2 +-----+---------+------+----------+----------+ 3 | nid | caption | nid | username | group_id | 4 +-----+---------+------+----------+----------+ 5 | 1 | dba | 1 | alex1 | 1 | 6 | 2 | ddd | NULL | NULL | NULL | 7 +-----+---------+------+----------+----------+ 8 2 rows in set (0.00 sec)
left join:isouter=True。
1 ret=session.query(Group).join(User,isouter=True).all() 2 print(ret)
如果想使用right join的话 把类颠倒下即可。
1 ret=session.query(User).join(Group,isouter=True).all() 2 print(ret)
如果连表查询的结果都是对User里的user表的操作,我们需要时Group里的表的内容。可以进行如下操作,在query()里添加我们想要操作的表对应的类。
1 ret=session.query(User,Group).join(Group).all() 2 sql=session.query(User,Group).join(Group) 3 print(ret) 4 print(sql) 5 [(alex1-1, 1-dba)] 6 SELECT "user".nid AS user_nid, "user".username AS user_username, "user".group_id AS user_group_id, cc.nid AS cc_nid, cc.caption AS cc_caption 7 FROM "user" JOIN cc ON cc.nid = "user".group_id
上面默认是把Group的cc表里的caption=User.group_id里的所有数据输出。
如果只想要对应的字段可以query()里指定想要的字段:
1 ret=session.query(User.username,Group.caption).join(Group).all() 2 sql=session.query(User,Group).join(Group) 3 print(ret) 4 print(sql) 5 [('alex1', 'dba')] 6 SELECT "user".nid AS user_nid, "user".username AS user_username, "user".group_id AS user_group_id, cc.nid AS cc_nid, cc.caption AS cc_caption 7 FROM "user" JOIN cc ON cc.nid = "user".group_id
relationship 查询:
如上的操作对于SQLALchemy来说,还是有些麻烦,于是就就有:relationship()来方便我们进行查询。他只是方便我们查询,对表结构无任何影响。
在哪个表里设置外键,一般就在那个表里设置关系(relationship),这样我们就可以进行更为简单的查询。
1 class Group(Base): 2 __tablename__ = 'cc' 3 nid = Column(Integer, primary_key=True,autoincrement=True) 4 caption = Column(String(32)) 5 6 class User(Base): 7 __tablename__ = 'user' 8 nid = Column(Integer, primary_key=True,autoincrement=True) 9 username = Column(String(32)) 10 group_id = Column(Integer,ForeignKey('cc.nid')) 11 group=relationship("Group",backref="user") 12 Session = sessionmaker(bind=engine) 13 session = Session() 14 sql=session.query(User) 15 print(sql) 16 SELECT "user".nid AS user_nid, "user".username AS user_username, "user".group_id AS user_group_id FROM "user"
1 ret=session.query(User).all() 2 for i in ret: 3 print(i.group.caption) 4 dba 5 ddd
如上的查询是正向查询。
1 ret=session.query(Group).filter(Group.caption=="dba").first() 2 print(ret.user) 3 for i in ret.user: 4 print(i.username,i.group_id) 5 [<__main__.User object at 0x0349C850>] 6 alex1 1
如上是反向查询。
说明:
column_name=relationship("B表名","B表新添加虚拟列")
column_name是表A的为了查询建立的虚拟的列。实际表结构中不存在这个列。这个列是B的对象的集合。可以通过这个列获取B表的中相应的列的值。如上表。
1 res=session.query(User).all() 2 print(res) 3 for i in res: 4 print(i.group.caption)
relationship("B表名","B表新添加虚拟列")中的"B表新添加虚拟列",我简称为B列。也就是说B表添加一个虚拟的列B,虚拟B列是A表的对象集合。通过B列可以查询出A表的值。
1 ret=session.query(Group).all() 2 print(ret) 3 for i in ret: 4 for j in i.user: 5 print(j.username) 6 alex1 7 alex2
注意是2个for循环。因为ret是Group的对象列表,而列表中的对象的user列是User的对象集合。所以进行2次循环。
总结:relationship是方便查询,在一对多;表结构中分别创建了一个虚拟关系列,方便查询。
三:多对多:表查询
结构:多对多关系中,最简单的是由三张表组成。第三张表是关系表。其他表和这张关系表的关系是一对多的关系。
表A和表B通过第三张表C建立关系。
创建多对多表结构:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy import Column, Integer, String, ForeignKey 3 from sqlalchemy.orm import sessionmaker,relationship 4 from sqlalchemy import create_engine 5 6 engine = create_engine("mysql+pymysql://root:@192.168.1.105:3306/s12", max_overflow=5) 7 Base = declarative_base() 8 9 class System_user(Base): 10 __tablename__="system_user" 11 username=Column(String(30)) 12 nid=Column(Integer,autoincrement=True,primary_key=True) 13 14 15 class Host(Base): 16 __tablename__="host" 17 nid=Column(Integer,autoincrement=True,primary_key=True) 18 ip=Column(String(30)) 19 20 21 class SystemuserToHost(Base): 22 __tablename__="systemusertohost" 23 nid=Column(Integer,autoincrement=True,primary_key=True) 24 sys_user_id=(Integer,ForeignKey("system_user.nid")) 25 host_id=Column(Integer,ForeignKey("host.nid")) 26 27 Base.metadata.create_all(engine) 28 29 30 mysql> show tables; 31 +------------------+ 32 | Tables_in_s12 | 33 +------------------+ 34 | host | 35 | system_user | 36 | systemusertohost | 37 +------------------+ 38 3 rows in set (0.00 sec)
插入数据:
1 def add_user(): 2 session.add_all( 3 (System_user(username="evil"), 4 System_user(username="tom"), 5 System_user(username="root"), 6 System_user(username="admin"), 7 8 ) 9 ) 10 session.commit() 11 def add_host(): 12 session.add_all( 13 (Host(ip="172.17.11.12"), 14 Host(ip="172.17.11.13"), 15 Host(ip="172.17.11.14"), 16 Host(ip="172.17.11.15"), 17 ) 18 ) 19 session.commit() 20 21 def add_systemusertohost(): 22 session.add_all( 23 (SystemuserToHost(sys_us_id=1,host_id=1), 24 SystemuserToHost(sys_us_id=2,host_id=1), 25 SystemuserToHost(sys_us_id=3,host_id=1), 26 SystemuserToHost(sys_us_id=1,host_id=2), 27 SystemuserToHost(sys_us_id=1,host_id=3), 28 SystemuserToHost(sys_us_id=2,host_id=4), 29 30 ) 31 ) 32 session.commit() 33 34 add_user() 35 add_host() 36 add_systemusertohost()
需求:ip=172.17.11.12 的主机上的用户都有什么?
按之前的查询:
1 ret_2=session.query(Host.nid).filter(Host.ip=="172.17.11.12").first() 2 print(ret_2[0]) 3 ret=session.query(SystemuserToHost.sys_us_id).filter(SystemuserToHost.host_id==ret_2[0]).all() 4 for i in ret: 5 print(i) 6 list_user=zip(*ret)#ret=((1,),(2,),(3))将ret转换成(1,2,3)的迭代器。 7 8 list_user=list(list_user)[0]#转换成列表。 9 10 ret_1=session.query(System_user.username).filter(System_user.nid.in_(list_user)).all() 11 print(ret_1)
1 1 2 (1,) 3 (2,) 4 (3,) 5 [('evil',), ('tom',), ('root',)
1)首先需要从Host中找指定IP=172.17.11.12 的对应nid。
2)从SystemuserToHost中找到对应的user_id
3)然后从System_user中找到对应的用户列表。
方法一:relationship建立在关系表中:
建立查询关系(relationship):
1 class SystemuserToHost(Base): 2 __tablename__="systemusertohost" 3 nid=Column(Integer,autoincrement=True,primary_key=True) 4 sys_us_id=Column(Integer,ForeignKey("system_user.nid")) 5 sys=relationship("System_user",backref="uu") 6 host_id=Column(Integer,ForeignKey("host.nid")) 7 host=relationship("Host",backref="host") 8 9 10 Session=sessionmaker(bind=engine) 11 session=Session() 12 ret=session.query(Host).filter(Host.ip=="172.17.11.12").first() 13 print(ret.host)#生成SystemuserToHost的对象集合。然后通过sys列找到username。 14 for i in ret.host: 15 print(i.sys.username) 16 evil 17 tom 18 root
思想:通过第三张关系C表和其他两张表建立外键,然后通过关系表和其他两张表建立关系(relationship),A表通过建立查询关系虚拟列A,映射到关系表虚拟列C,虚拟列C中包含B表的对象集合,直接映射到想要得到的B表的列值。
二:relationship建立在表A中:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy import Column, Integer, String, ForeignKey 3 from sqlalchemy.orm import sessionmaker,relationship 4 from sqlalchemy import create_engine 5 6 engine = create_engine("mysql+pymysql://root:@192.168.1.105:3306/s12", max_overflow=5) 7 Base = declarative_base() 8 9 class System_user(Base): 10 __tablename__="system_user" 11 nid=Column(Integer,autoincrement=True,primary_key=True) 12 username=Column(String(30)) 13 14 15 16 class Host(Base): 17 __tablename__="host" 18 nid=Column(Integer,autoincrement=True,primary_key=True) 19 ip=Column(String(30)) 20 host_u=relationship("System_user",secondary=lambda:SystemuserToHost.__table__,backref="h")#注意需要写通过那个表(secondary=lambda:SystemuserToHost.__table__)和System_user建立关系。注意secondary=后面跟的是对象。如果没有lambda需要把类SystemuserToHost写在前面。 21 22 class SystemuserToHost(Base): 23 __tablename__="systemusertohost" 24 nid=Column(Integer,autoincrement=True,primary_key=True) 25 sys_us_id=Column(Integer,ForeignKey("system_user.nid")) 26 host_id=Column(Integer,ForeignKey("host.nid")) 27 28 29 30 Session=sessionmaker(bind=engine) 31 session=Session() 32 ret=session.query(Host).filter(Host.ip=="172.17.11.12").first() 33 for i in ret.host_u: 34 print(i.username) 35 evil 36 tom 37 root
注意需要写通过那个表(secondary=lambda:SystemuserToHost.__table__)和System_user建立关系。注意secondary=后面跟的是对象。如果没有lambda需要把类SystemuserToHost写在前面。SystemuserToHost未定义。
二:paramiko
上篇文章已经详细介绍paramiko了。今天在进一步研究一下:
一:需求:当我们需要在主机上串行执行命令.
实现:
1 import paramiko 2 import uuid 3 4 class SSHConnection(object): 5 6 def __init__(self, host='192.168.11.61', port=22, username='alex',pwd='alex3714'): 7 self.host = host 8 self.port = port 9 self.username = username 10 self.pwd = pwd 11 self.__k = None 12 13 def run(self): 14 self.connect() 15 pass 16 self.close() 17 18 def connect(self): 19 transport = paramiko.Transport((self.host,self.port)) 20 transport.connect(username=self.username,password=self.pwd) 21 self.__transport = transport 22 23 def close(self): 24 self.__transport.close() 25 26 def cmd(self, command): 27 ssh = paramiko.SSHClient() 28 ssh._transport = self.__transport 29 # 执行命令 30 stdin, stdout, stderr = ssh.exec_command(command) 31 # 获取命令结果 32 result = stdout.read() 33 return result 34 35 def upload(self,local_path, target_path): 36 # 连接,上传 37 sftp = paramiko.SFTPClient.from_transport(self.__transport) 38 # 将location.py 上传至服务器 /tmp/test.py 39 sftp.put(local_path, target_path) 40 41 ssh = SSHConnection() 42 ssh.connect() 43 r1 = ssh.cmd('df') 44 ssh.upload('s2.py', "/home/evil/s7.py") 45 ssh.close() 46 47
二:需求:实现ssh登录终端:
实现:
1 import paramiko 2 import sys 3 import os 4 import socket 5 import getpass 6 7 from paramiko.py3compat import u 8 9 # windows does not have termios... 10 try: 11 import termios 12 import tty 13 has_termios = True#判断登录类型:True表示linux 14 except ImportError: 15 has_termios = False#Flse表示window 16 17 18 def interactive_shell(chan):#判断登录的主机类型,并调用相应的函数。 19 if has_termios: 20 posix_shell(chan)#linux 21 else: 22 windows_shell(chan)#window 23 24 25 def posix_shell(chan):#用select模式来监听终端输入设备变化。 26 import select 27 28 oldtty = termios.tcgetattr(sys.stdin) 29 try: 30 tty.setraw(sys.stdin.fileno()) 31 tty.setcbreak(sys.stdin.fileno()) 32 chan.settimeout(0.0) 33 log = open('handle.log', 'a+', encoding='utf-8')#写入操作命令日志。 34 flag = False 35 temp_list = [] 36 while True: 37 r, w, e = select.select([chan, sys.stdin], [], []) 38 if chan in r: 39 try: 40 x = u(chan.recv(1024)) 41 if len(x) == 0: 42 sys.stdout.write('\r\n*** EOF\r\n') 43 break 44 if flag: 45 if x.startswith('\r\n'): 46 pass 47 else: 48 temp_list.append(x) 49 flag = False 50 sys.stdout.write(x) 51 sys.stdout.flush() 52 except socket.timeout: 53 pass 54 if sys.stdin in r: 55 x = sys.stdin.read(1) 56 import json 57 58 if len(x) == 0: 59 break 60 61 if x == '\t': 62 flag = True 63 else: 64 temp_list.append(x) 65 if x == '\r': 66 log.write(''.join(temp_list)) 67 log.flush() 68 temp_list.clear() 69 chan.send(x) 70 71 finally: 72 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) 73 74 75 def windows_shell(chan):#window 执行函数。 76 import threading 77 78 sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") 79 80 def writeall(sock): 81 while True: 82 data = sock.recv(256) 83 if not data: 84 sys.stdout.write('\r\n*** EOF ***\r\n\r\n') 85 sys.stdout.flush() 86 break 87 sys.stdout.write(data) 88 sys.stdout.flush() 89 90 writer = threading.Thread(target=writeall, args=(chan,)) 91 writer.start() 92 93 try: 94 while True: 95 d = sys.stdin.read(1) 96 if not d: 97 break 98 chan.send(d) 99 except EOFError: 100 # user hit ^Z or F6 101 pass 102 103 104 def run():#主调用函数。 105 default_username = getpass.getuser() 106 username = input('Username [%s]: ' % default_username) 107 if len(username) == 0: 108 username = default_username 109 110 111 hostname = input('Hostname: ') 112 if len(hostname) == 0: 113 print('*** Hostname required.') 114 sys.exit(1) 115 116 tran = paramiko.Transport((hostname, 22,)) 117 tran.start_client() 118 119 default_auth = "p" 120 auth = input('Auth by (p)assword or (r)sa key[%s] ' % default_auth) 121 if len(auth) == 0: 122 auth = default_auth 123 124 if auth == 'r': 125 default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa') 126 path = input('RSA key [%s]: ' % default_path) 127 if len(path) == 0: 128 path = default_path 129 try: 130 key = paramiko.RSAKey.from_private_key_file(path) 131 except paramiko.PasswordRequiredException: 132 password = getpass.getpass('RSA key password: ') 133 key = paramiko.RSAKey.from_private_key_file(path, password) 134 tran.auth_publickey(username, key) 135 else: 136 pw = getpass.getpass('Password for %s@%s: ' % (username, hostname)) 137 tran.auth_password(username, pw) 138 139 # 打开一个通道 140 chan = tran.open_session() 141 # 获取一个终端 142 chan.get_pty() 143 # 激活器 144 chan.invoke_shell() 145 146 interactive_shell(chan) 147 148 chan.close() 149 tran.close() 150 151 152 if __name__ == '__main__': 153 run() 154 155
基于第二个需求,我们可以实现堡垒机登录:
1 堡垒机执行流程: 2 3 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码) 4 用户登陆堡垒机,输入堡垒机用户名密码,现实当前用户管理的服务器列表 5 用户选择服务器,并自动登陆 6 执行操作并同时将用户操作记录
表结构设计:
代码:(自己写的)
orm.py code
1 #/usr/bin/ecv python 2 #author:evil_liu 3 #date:20160804 4 #python_version:python3.x 5 #description:this molude is used for create database and table. 6 from sqlalchemy.ext.declarative import declarative_base 7 from sqlalchemy import Column, Integer, String, ForeignKey,Date 8 from sqlalchemy.orm import sessionmaker, relationship 9 from sqlalchemy import create_engine 10 import hashlib 11 import os 12 import sys 13 import getpass 14 BASE_Dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 15 sys.path.append(BASE_Dir) 16 from lib import ssh 17 from conf import log 18 import datetime 19 user_menu=''' 20 +---------------------+ 21 |1:Add User | 22 |2:Login Host | 23 |3:Dlete User | 24 |4:Check Host | 25 |5:Check TeamMember | 26 |6:Exit | 27 +---------------------+ 28 '''#用户操作菜单 29 30 engine = create_engine("mysql+pymysql://root:@192.168.1.106:3306/homework_day13", max_overflow=5) 31 Base = declarative_base() #自定义的类声明sqlorm基类。 32 33 class Host(Base): 34 ''' 35 功能:该类是功能创建主机列表的。 36 ''' 37 __tablename__="host" 38 nid=Column(Integer,autoincrement=True,primary_key=True) 39 ip=Column(String(32)) 40 port=Column(String(12)) 41 42 class Systme_User(Base): 43 ''' 44 功能:该类主要创建系统用户表格。 45 ''' 46 __tablename__="system_user" 47 nid=Column(Integer,autoincrement=True,primary_key=True) 48 username=Column(String(32)) 49 password=Column(String(32)) 50 51 class HostToSystme_User(Base): 52 ''' 53 功能:该类主要功能是创建主机和主机系统用户的关系表。 54 ''' 55 __tablename__="hosttosystem_user" 56 nid=Column(Integer,autoincrement=True,primary_key=True) 57 host_id=Column(Integer,ForeignKey("host.nid")) 58 host_user_id=Column(Integer,ForeignKey("system_user.nid")) 59 user=relationship("Systme_User",backref="uu") 60 host=relationship("Host",backref="h") 61 62 63 class Board_User(Base): 64 ''' 65 功能:该类主要功能是创建堡垒机登陆用户表。 66 ''' 67 __tablename__="board_user" 68 nid=Column(Integer,autoincrement=True,primary_key=True) 69 username=Column(String(32)) 70 pasword=Column(String(32)) 71 user_status=Column(Integer)##用户的账号状态,为1的时候表示使用状态,为0是锁定状态不能登陆。 72 user_type=Column(Integer)#堡垒机用户类型。 73 group_id=Column(Integer,ForeignKey("board_group.nid")) 74 team=relationship("Board_Group",backref="g") 75 76 class HostToBoard_User(Base): 77 ''' 78 功能:堡垒机用户和主机列表多对多的对应关系表。 79 ''' 80 __tablename__='hosttoboard_user' 81 nid=Column(Integer,autoincrement=True,primary_key=True) 82 host_id=Column(Integer,ForeignKey("host.nid")) 83 host=relationship("Host",backref="hh") 84 board_user_id=Column(Integer,ForeignKey('board_user.nid')) 85 board_u=relationship("Board_User",backref="u") 86 87 88 89 class Board_Group(Base): 90 ''' 91 功能:该类主要是创建堡垒机用户所在的组。 92 ''' 93 __tablename__="board_group" 94 nid=Column(Integer,autoincrement=True,primary_key=True) 95 group_name=Column(String(32)) 96 97 class Log_Record(Base): 98 ''' 99 功能:该类主要创建日志表。 100 ''' 101 __tablename__='log_record' 102 nid=Column(Integer,autoincrement=True,primary_key=True) 103 user_name=Column(String(32)) 104 sys_user=Column(String(32)) 105 host=Column(String(32)) 106 cmd=Column(String(200)) 107 date=Column(String(33)) 108 109 110 Session=sessionmaker(bind=engine) 111 session=Session() 112 def add_host_data(): 113 ''' 114 功能:该函数主要是给host表里添加数据。 115 :return: 无。 116 ''' 117 session.add_all( 118 ( 119 Host(ip='172.17.33.75',port='22'), 120 Host(ip='172.17.33.76',port='22'), 121 Host(ip='172.17.33.77',port='22'), 122 Host(ip='192.168.1.106',port='22'), 123 ) 124 ) 125 session.commit() 126 127 def add_data_sysuser(): 128 ''' 129 功能:该函数主要作用是给system_user表添加数据。 130 :return: 无。 131 ''' 132 session.add_all( 133 ( 134 Systme_User(username='root',password='123'),#123 135 Systme_User(username='evil',password='123'), 136 Systme_User(username='tom',password='123'), 137 Systme_User(username='jack',password='123') 138 139 ) 140 ) 141 session.commit() 142 def add_data_hosttosystem_user(): 143 ''' 144 功能:该函数主要是给hosttosystem_user表添加数据。 145 :return: 146 ''' 147 session.add_all( 148 ( 149 HostToSystme_User(host_id=1,host_user_id=1), 150 HostToSystme_User(host_id=1,host_user_id=2), 151 HostToSystme_User(host_id=2,host_user_id=2), 152 HostToSystme_User(host_id=2,host_user_id=3), 153 HostToSystme_User(host_id=3,host_user_id=3), 154 HostToSystme_User(host_id=3,host_user_id=1), 155 HostToSystme_User(host_id=4,host_user_id=4), 156 HostToSystme_User(host_id=4,host_user_id=2), 157 ) 158 ) 159 session.commit() 160 161 def add_data_board_user(): 162 ''' 163 功能:该函数主要是给board_user表添加数据。 164 :return: 无。 165 ''' 166 session.add_all( 167 ( 168 Board_User(username='ella',pasword='202cb962ac59075b964b07152d234b70',group_id=1,user_type=2,user_status=1), 169 Board_User(username='roy',pasword='202cb962ac59075b964b07152d234b70',group_id=1,user_type=2,user_status=1), 170 Board_User(username='john',pasword='202cb962ac59075b964b07152d234b70',group_id=2,user_type=1,user_status=1), 171 Board_User(username='david',pasword='202cb962ac59075b964b07152d234b70',group_id=2,user_type=2,user_status=1), 172 Board_User(username='benson',pasword='202cb962ac59075b964b07152d234b70',group_id=3,user_type=2,user_status=1), 173 Board_User(username='adam',pasword='202cb962ac59075b964b07152d234b70',group_id=3,user_type=1,user_status=0), 174 ) 175 ) 176 session.commit() 177 178 def add_data_hosttoboard_user(): 179 ''' 180 功能:该函数主要给表hosttoboard_user添加数据。 181 :return: 无。 182 ''' 183 session.add_all( 184 ( 185 HostToBoard_User(board_user_id=1,host_id=1), 186 HostToBoard_User(board_user_id=1,host_id=2), 187 HostToBoard_User(board_user_id=1,host_id=3), 188 HostToBoard_User(board_user_id=2,host_id=2), 189 HostToBoard_User(board_user_id=2,host_id=3), 190 HostToBoard_User(board_user_id=3,host_id=4), 191 HostToBoard_User(board_user_id=4,host_id=2), 192 HostToBoard_User(board_user_id=4,host_id=1), 193 HostToBoard_User(board_user_id=5,host_id=1), 194 HostToBoard_User(board_user_id=5,host_id=3), 195 HostToBoard_User(board_user_id=6,host_id=1), 196 ) 197 ) 198 session.commit() 199 200 def add_data_board_group(): 201 ''' 202 功能:该函数主要作用是给board_group表添加数据。 203 :return:无。 204 ''' 205 session.add_all( 206 ( 207 Board_Group(group_name="DBA"), 208 Board_Group(group_name="NETWORK"), 209 Board_Group(group_name="OPERATION"), 210 ) 211 ) 212 session.commit() 213 def add_data_log_record(u,sys,ip,cmd,date): 214 session.add(Log_Record(user_name=u,sys_user=sys,host=ip,cmd=cmd,date=date)) 215 session.commit() 216 def init_db(): 217 ''' 218 功能:初始化数据库和表格。 219 :return: 无。 220 ''' 221 Base.metadata.create_all(engine) 222 add_host_data() 223 add_data_sysuser() 224 add_data_hosttosystem_user() 225 add_data_board_group() 226 add_data_board_user() 227 add_data_hosttoboard_user() 228 229 def hash(x): 230 ''' 231 功能:该函数主要是用户输入密码进行md5解析。 232 :return: 返回账号密码的解析的md5值。 233 ''' 234 M=hashlib.md5() 235 M.update(bytes(x,encoding='utf-8')) 236 return M.hexdigest() 237 def outer(func): 238 ''' 239 功能:该函数主要用户操作菜单权限验证。 240 :param func: 传入函数。 241 :return: 242 ''' 243 def inner(x,y): 244 if y==1: 245 ret=func(x,y) 246 return ret 247 else: 248 print('\033[31;1m %s \033[0m'%'Permission Denied !!!!') 249 return inner 250 def check_accout(user,pwd): 251 ''' 252 功能:该函数主要功能是验证用户的账号密码是否正确。 253 :param user: 用户账号。 254 :param pwd: 用户密码。 255 :return: True表示用户账号密码正确,反之错误。 256 ''' 257 res=session.query(Board_User).filter(Board_User.username==user).all() 258 log_obj=log.Logger("login.log","login")##记录用户登录日志。 259 if res: 260 if pwd==res[0].pasword and res[0].user_status==1:#查看用户账号是否锁定。 261 log_obj.log_in().info("the user %s login successful!"%user) 262 return res[0].user_type 263 else: 264 log_obj.log_in().info("the user %s login fail!"%user) 265 return False 266 else: 267 log_obj.log_in().info("the user %s login fail!"%user) 268 return False 269 @outer 270 def add_user(x,y): 271 ''' 272 功能:该函数主要实现管理员给堡垒机添加新用户。 273 :param x: 当前登录用户的名字。 274 :param y: 当前登录用户的类型。 275 :return: 276 ''' 277 host_list_id=[]#主机host_id的列表。 278 ret=session.query(Board_User).all() 279 booard_user_id_list=[i.username for i in ret]#生成堡垒机用户ID列表。 280 while True:#判断添加用户是否有效。 281 add_username=input("Entre add username>") 282 if add_username not in booard_user_id_list: 283 print("the username %s is vail!"%add_username) 284 break 285 else: 286 print("sorry the username: %s is exits,try another!"%add_username) 287 add_password=hash(input("Entre the user of password>").strip())#密码MD5加密。 288 ret=session.query(Board_Group).all() 289 nid_list=[i.nid for i in ret] 290 print("The user group list".center(30,"-")) 291 for i in ret: 292 print(i.nid,i.group_name) 293 while True:#用户输入的group_id是否合法。 294 add_group_id=input("Entre the group of number for user>") 295 if add_group_id.isdigit() and int(add_group_id) in nid_list: 296 break 297 else: 298 print("input invalid number !") 299 continue 300 ret_1=session.query(Host).all() 301 host_id_list=[i.nid for i in ret_1] 302 print("The Host IP LIST".center(45,"+")) 303 for i in ret_1:#输出主机的nid和主机IP 304 print(i.nid,i.ip) 305 while True:#可以进行选择多个主机给一个堡垒机用户。 306 host_id=input("Entre the host number for the user or entre q exit > ") 307 if host_id.isdigit() and int(host_id) in host_id_list: 308 if int(host_id) in host_list_id:#避免管理员给用户添加的主机的多次的情况。 309 print("Do not add the same IP for user!try again") 310 continue 311 else: 312 print("the IP add successful!") 313 host_list_id.append(int(host_id)) 314 continue 315 elif host_id=='q': 316 break 317 else: 318 print("sorry you entre a invalid number!try again.") 319 continue 320 while True: 321 add_user_type=input("Entre user_type:1:admin 2:common user >") 322 if add_user_type.isdigit() and add_user_type in ["1","2"]: 323 break 324 else: 325 print("sorry you input invalid number! tyr again.") 326 #往board_user表里插值.默认添加的用户的用户类型只能普通用户。 327 session.add(Board_User(username=add_username,pasword=add_password, 328 group_id=int(add_group_id),user_type=int(add_user_type),user_status=1)) 329 session.commit() 330 nid=session.query(Board_User.nid).filter(Board_User.username==add_username).first() 331 for k in host_list_id:#往add_data_hosttoboard_user插值。 332 session.add(HostToBoard_User(board_user_id=nid[0],host_id=k)) 333 session.commit() 334 print("add the username:%s successful!"%add_username) 335 336 def login_host(x,y): 337 ''' 338 功能:该函数主要是当前登录堡垒机用户,登录主机。 339 :param x: 用户名。 340 :param y: 用户类型。 341 :return: 342 ''' 343 ret=session.query(Board_User).filter(Board_User.username==x).first() 344 print("%s"%'host list'.center(30,"*")) 345 host_list=[i.host.ip for i in ret.u] 346 for i,j in enumerate(host_list,1): 347 print(i,j) 348 print('*'*30) 349 choice_1=input("Please entre number which host you want to login >") 350 if choice_1.isdigit and 0< int(choice_1) <len(host_list)+1: 351 host_ip=host_list[int(choice_1)-1]#主机IP。 352 ret=session.query(Host).filter(Host.ip==host_ip).all() 353 username_list=[i.user.username for i in ret[0].h] 354 print("%s"%'username list'.center(30,'-')) 355 for i ,j in enumerate(username_list,1): 356 print(i,j) 357 print('-'*30) 358 choice_2=input("Entre number which user you want to login > ") 359 if choice_2.isdigit and 0 <int(choice_2) < len(username_list)+1: 360 user=username_list[int(choice_2)-1] 361 pwd=session.query(Systme_User.password).filter(Systme_User.username==user).first() 362 ret_3=ssh.run(host_ip,user,pwd[0])#调用ssh模块。进行主机登录。 363 add_data_log_record(x,user,host_ip,ret_3,datetime.datetime.now())#将用户操作的记录,写入数据库。 364 exit(0) 365 else: 366 print("sorry you entre invalid number!") 367 else: 368 print("sorry you entre invalid number!") 369 @outer 370 def delet_user(x,y): 371 ''' 372 功能:该函数主要实现管理删除堡垒机用户。通过锁定用户状态,来实现用户的删除。1表示登陆状态,0表示锁定状态。 373 :param x: 当前登录用户。 374 :param y: 当前用户类型。 375 :return: 无。 376 ''' 377 username_list=[] 378 ret=session.query(Board_User).filter(Board_User.user_status==1).all()#输出所有未锁定用户。 379 print("%s"%'the user list'.center(45,'*')) 380 for i in ret: 381 print(i.username) 382 username_list.append(i.username) 383 while True:#判断用户输入的用户名是否合法。 384 lock_user=input("Entre the username which you want to delete >").strip() 385 if lock_user not in username_list: 386 print("sorry you entre invalid username try again!") 387 else: 388 break 389 session.query(Board_User).filter(Board_User.username==lock_user).update({"user_status":0})#对堡垒机用户进行锁定。 390 session.commit() 391 print("operation successful!") 392 393 394 def check_host(x,y): 395 ''' 396 功能:该函数主要作用是查看当前登录的堡垒机用户的下得服务器列表。 397 :param x: 用户名。 398 :param y: 用户类型。 399 :return: 无。 400 ''' 401 ret=session.query(Board_User).filter(Board_User.username==x).first() 402 print("%s"%'host list'.center(30,"*")) 403 for i in ret.u: 404 print(i.host.ip) 405 print('*'*30) 406 def check_team_member(x,y): 407 ''' 408 功能:该函数主要作用查看当前用户所在组的成员。 409 :param x: 登录用户。 410 :param y: 当前用户类型。 411 :return: 无。 412 ''' 413 ret=session.query(Board_User).filter(Board_User.username==x).first() 414 mem=session.query(Board_User.username).filter(Board_User.group_id==ret.group_id, 415 Board_User.user_status==1).all()#查找未锁定用户。 416 print("%s"%"TeamMember list".center(20,'-')) 417 for i in mem: 418 print(i[0]) 419 print("-"*20) 420 men_func_dic={ 421 '1':add_user, 422 '2':login_host, 423 '3':delet_user, 424 '4':check_host, 425 '5':check_team_member, 426 }#用户菜单映射。 427 def oper_menu(usrname,ret): 428 ''' 429 功能:该函数用户操作函数。 430 :param usrname: 用户名。 431 :param ret: 用户类型。 432 :return: 无。 433 ''' 434 log_obj=log.Logger("command.log","command") 435 while True: 436 print(user_menu) 437 choice=input("Entre your choice >") 438 if choice.isdigit() and 0<int(choice) <6: 439 men_func_dic[choice](usrname,ret) 440 log_obj.log_in().info(" the user excute command %s"%men_func_dic[choice].__name__) 441 else: 442 print("goobye") 443 exit(0) 444 def main(): 445 ''' 446 功能:该模块的主调用函数。 447 :return: 无。 448 ''' 449 choice_2=input("if you first run this program,please Initializate database?(yes or no)").strip() 450 if choice_2=="yes": 451 init_db() 452 while True: 453 usrname=input("Entre your login username >") 454 password=hash(getpass.getpass("Entre your login password >"))#用户密码MD5验证。 455 ret=check_accout(usrname,password) 456 if ret: 457 oper_menu(usrname,ret) 458 else: 459 print("your username or passowrd is wrong, try again!") 460 continue 461 if __name__ == '__main__': 462 main()