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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM