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

 

posted @ 2016-08-02 11:16  evil_liu  阅读(5536)  评论(0编辑  收藏  举报