本節內容:
一:外鍵存在的意義:
任何的數據都可以在一個表中存儲,但是這樣存儲有這個問題:如果一個字段在一個表里多次出現,而且這個字段的長度比較大,那么將會在存儲上有浪費。
這個時間如果出現另一張表,存儲他們之間的關系,是不是更好呢?
但是如果這樣做,還會出現一個問題,比如: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()