zoukankan      html  css  js  c++  java
  • Django项目:堡垒机(Linux服务器主机管理系统)--03--03堡垒机在Linux系统里记录会话日志02/02

      1 #main.py
      2 #本文件写所有的连接交互动作程序
      3 # ————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
      4 from Fortress import models #数据库
      5 import subprocess#系统交互#subprocess模块允许我们创建子进程,连接他们的输入/输出/错误管道,还有获得返回值。
      6 import uuid #通用唯一识别码
      7 import os #系统
      8 BASE_DIR = os.path.dirname(os.path.abspath(__file__))# 动态路径,当前文件的父目录
      9 #E:堡垒机eyeFortressackend
     10 # ————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
     11 #————————————————02堡垒机设计后台交互程序 开始————————————————
     12 from django.contrib.auth import authenticate #验证用户的证书
     13 import getpass#输入密码时不显示密码
     14 #用户登陆堡垒机后的交互程序
     15 class HostManager(object):
     16     def __init__(self):
     17         self.user = None #当前账号
     18     # ————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
     19     def get_session_id(self,bind_host_obj,uuid):#创建应用会话的id
     20         print('创建新的会话:',self.user.id,bind_host_obj,uuid)
     21         session_obj = models.Session(user_id = self.user.id,bind_host=bind_host_obj,uuid=uuid)#创建数据库
     22         session_obj.save()#会话保存
     23         print('新的会话的ID:',session_obj.id)
     24         return session_obj#返回会话
     25     # ————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
     26     def interactive(self):#交互脚本
     27         # ————————————————登录堡垒机 开始————————————————
     28         print("----开始 运行交互脚本----")
     29         count = 0 #用户名密码试错的次数
     30         while count < 6:#用户名密码试错的次数
     31             username = input("请输入你的用户名:").strip()#用户名
     32             password = getpass.getpass("请输入你的密码:").strip()#密码
     33             user = authenticate(username=username,password=password)#验证用户名密码
     34             if user: #验证成功
     35                 print("欢迎 %s 登陆使用!".center(50,'-') % user.name )
     36                 self.user = user #验证成功,赋值
     37                 break #退出无限循环
     38             else:
     39                 print("用户名或密码错误!")
     40             count += 1#用户名密码试错的次数
     41         else:
     42             print("----结束 运行交互脚本----")
     43             exit("用户名密码试错的次数到了,再见。")
     44         """
     45         #————从外部调用django的方法 开始————
     46         #注意写,从外部调用django的方法 E:堡垒机eyefortess_manage.py
     47         #注意,到admin后台配置主机给账户
     48         # ————————————
     49         #运行 Terminal
     50         #E:堡垒机eye>python fortess_manage.py
     51         #Username:admin@qq.com
     52         #Password:admin123456
     53         #————从外部调用django的方法 结束————
     54         """
     55         # ————————————————登录堡垒机 结束————————————————
     56         # ——————————————选择要连接的主机 开始————————————
     57         if self.user: #验证登录成功
     58             while True:
     59                 #————————打印当前账户所有的可以登录主机 开始——————
     60                 for index,host_group  in enumerate(self.user.host_groups.all()): #select_related()
     61                     print("%s.	%s[%s]" %(index,host_group.name, host_group.bind_hosts.count()))
     62                 print("z.	未分组主机[%s]" %(self.user.bind_hosts.count()))
     63                 #————————打印当前账户所有的可以登录主机 结束——————
     64                 # ————————开始选择分组 开始——————
     65                 choice = input("%s>>:"% self.user).strip()#输入选择
     66                 if len(choice) == 0:#长度等于0
     67                     continue#跳过这次循环
     68                 selected_host_group = None #选择分组
     69                 if choice.isdigit():#检测字符串是否只由数字组成
     70                     choice = int(choice)#转成整数
     71                     if choice >=0 and choice <= index: #判断输入的选项是否合法
     72                         selected_host_group = self.user.host_groups.all()[choice]#迭代选择分组
     73                 elif choice == 'z':#未分组
     74                     selected_host_group = self.user
     75                 # ————————开始选择分组 结束——————
     76                 # ————————开始选择主机 开始——————
     77                 if selected_host_group: #选择分组后选择主机
     78                     while True:
     79                         print("当前选择的分组: %s ".center(50,'-') %selected_host_group)
     80                         # ————————打印分组里的所有主机 开始——————
     81                         for index, bind_host in enumerate(selected_host_group.bind_hosts.all()):
     82                             print("%s.	%s" % (index, bind_host))
     83                         print("返回上级菜单,请按  b  ")
     84                         # ————————打印分组里的所有主机 结束——————
     85                         # ————————选择分组里的主机 开始——————
     86                         choice = input("%s>>>:" % self.user).strip()#输入选择
     87                         if choice.isdigit():#检测字符串是否只由数字组成
     88                             choice = int(choice)#转成整数
     89                             if choice >= 0 and choice <= index:#判断输入的选项是否合法
     90                                 bind_host = selected_host_group.bind_hosts.all()[choice]#迭代选择主机
     91                                 print("准备登录 %s 主机".center(50,'-') %bind_host)
     92                         # ————————选择分组里的主机 结束——————
     93                                 # ————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
     94                                 if bind_host.host.enabled:#是否启用了此主机
     95                                     ssh_uuid = uuid.uuid4()  # 唯一标识
     96                                     session_obj = self.get_session_id(bind_host, ssh_uuid)  # 创建应用会话的id
     97                                     print("创建会话审计记录,用ID关联shell脚本创建的文件名:", session_obj)
     98                                     # ————————在Linux系统下打开新进程的shell监控脚本 开始————————
     99                                     monitor_script = subprocess.Popen("sh %s/session_tracker.sh %s %s"
    100                                                                       % (BASE_DIR,  # 动态路径,当前文件的父目录
    101                                                                          ssh_uuid,  # 唯一标识
    102                                                                          session_obj.id),#新的会话的ID
    103                                                                          shell=True,#如果把shell设置成True,指定的命令会在shell里解释执行。
    104                                                                          stdout=subprocess.PIPE,  # subprocess 实现管道( pipe )交互操作读/写通信
    105                                                                          stderr=subprocess.PIPE)  # stdout, stdin, stderr的中文名字分别是标准输出,标准输入和标准错误。
    106                                     print('监控执行的脚本:', monitor_script)
    107                                     # print('读取标准错误,很慢,测试时才用:',monitor_script.stderr.read())
    108                                     # ————————在Linux系统下打开新进程的shell监控脚本 结束————————
    109                                     # ————————只能在Linux系统下运行 开始————————
    110                                     print('准备连接远程主机......')
    111                                     cmd_str = "sshpass -p %s ssh -p %s %s@%s -E %s -o StrictHostKeyChecking=no" % (
    112                                         bind_host.remote_user.password,  # 登录的密码
    113                                         bind_host.host.port,  # 登录的IP端口
    114                                         bind_host.remote_user.username,  # 登录的账号
    115                                         bind_host.host.ip_addr,  # 登录的IP地址
    116                                         ssh_uuid)  # 唯一标识
    117                                     # sshpass 可以在命令行直接使用密码来进行远程连接和远程拉取文件。只能在Linux系统下运行。
    118                                     #  -E 临时文件名 -o StrictHostKeyChecking=no 忽略登陆确认提示
    119                                     print('要执行的命令:', cmd_str)#实际生产时关闭
    120                                     # 要执行的命令: sshpass -p 123456 ssh -p 22  root@192.192.168.127 -E a1255ca0-2ed8-4a56-866b-758e96d871d5 -o StrictHostKeyChecking=no
    121                                     subprocess.run(cmd_str, shell=True)  # 只能在Linux系统下运行
    122                                     # ————————只能在Linux系统下运行 结束————————
    123                                 else:
    124                                     print('此主机未启用,请联系管理员!!')
    125                                 # ————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
    126                         elif choice == 'b':#退出
    127                             break #终止无限循环
    128                 # ————————开始选择主机 结束——————
    129         # ——————————————选择要连接的主机 结束————————————
    130 #————————————————02堡垒机设计后台交互程序 结束————————————————
    131 
    132 
    133 
    134 """
    135 ————————安装django 开始————————
    136 login as: root 
    137 root@192.192.168.126's password: 123456
    138 [root@localhost ~]# python3
    139 >>> import django
    140 >>> exit()
    141 [root@localhost ~]# pip3 install --upgrade pip
    142 [root@localhost ~]# pip3 install --default-timeout=1000 Django==2.1.4
    143 [root@localhost ~]# python3
    144 >>> import django
    145 >>> print(django.get_version())
    146 2.1.4
    147 >>> exit()
    148 ————————安装django 结束————————
    149 """#安装django
    150 """
    151 ————————安装strace跟踪线程 开始————————
    152 [root@localhost ~]# yum -y install strace           
    153 [root@localhost ~]# ssh root@192.192.168.110        |
    154 root@192.192.168.110's password:123456              |
    155                                                     | [root@localhost ~]# ps -ef
    156                                                     | UID        PID  PPID  C STIME TTY          TIME CMD
    157                                                     | root      5999  5936  0 08:06 pts/2    00:00:00 ssh root@192.192.168.110
    158                                                     | [root@localhost ~]# sudo strace -p 5999
    159 [root@localhost ~]# l                               | read(4, "l", 16384)                     = 1
    160                                                     | [root@localhost ~]# sudo strace -p 5999 -o eye.log
    161 [root@localhost ~]# A                               | 
    162                                                     | [root@localhost ~]# cat eye.log                                    
    163                                                     | [root@localhost ~]# cat eye.log |grep 'read(4'
    164                                                     | read(4, "A", 16384)                     = 1
    165 ————————安装strace跟踪线程 结束————————
    166 """#安装strace跟踪线程
    167 """
    168 ————————sshpass可以在命令行直接使用密码来进行远程连接和远程拉取文件 开始————————
    169 [root@localhost ~]# yum -y install sshpass
    170 [root@localhost ~]# sshpass -p 123456 ssh -p 22 root@192.192.168.110
    171 [root@localhost ~]# ip addr
    172 ————————用python调用sshpass 开始————————
    173 [root@localhost ~]# python3
    174 >>> import subprocess
    175 >>> subprocess.run("sshpass -p 123456 ssh root@192.192.168.110 -o StrictHostKeyChecking=no",shell=True)
    176 ————————另启一个会话查看————————
    177 [root@localhost ~]# ps -ef
    178 UID        PID  PPID  C STIME TTY          TIME CMD
    179 root      5147  5146  0 09:41 pts/8    00:00:00 ssh root@192.192.168.110 -o StrictHostKeyCh
    180 [root@localhost ~]# sudo strace -fp 5147 -t -o eye.log
    181 [root@localhost ~]# cat eye.log  
    182 ————获取进程号 开始————
    183 [root@localhost ~]# ps -ef | grep 'root@192.192.168.110'
    184 [root@localhost ~]# ps -ef | grep 'root@192.192.168.110' | grep ssh
    185 [root@localhost ~]# ps -ef | grep 'root@192.192.168.110' | grep ssh | grep -v sshpass
    186 [root@localhost ~]# ps -ef | grep 'root@192.192.168.110' | grep ssh | grep -v sshpass | awk '{ print $2 }'
    187 ————获取进程号 结束————
    188 ————————用python调用sshpass 结束————————
    189 ————————sshpass可以在命令行直接使用密码来进行远程连接和远程拉取文件 结束————————
    190 """#sshpass
    191 """
    192 ————————获取进程号,唯一标识 开始————————
    193 [root@localhost ~]# python3
    194 >>> import subprocess
    195 >>> subprocess.run("sshpass -p 123456 ssh root@192.192.168.110 -E wybs123 -o StrictHostKeyChecking=no",shell=True)
    196 ————————另启一个登陆会话————————
    197 [root@localhost ~]# python3
    198 >>> import subprocess
    199 >>> subprocess.run("sshpass -p 123456 ssh root@192.192.168.110 -E wybs456 -o StrictHostKeyChecking=no",shell=True)
    200 ————————另启一个会话查看————————
    201 [root@localhost ~]# ps -ef | grep 'root@192.192.168.110' | grep ssh | grep -v sshpass | grep  wybs123 | awk '{ print $2 }'    
    202 [root@localhost ~]# ps -ef | grep  wybs123 | grep ssh | grep -v sshpass | awk '{ print $2 }'    
    203 ————————获取进程号,唯一标识  结束————————
    204 """#获取进程号
    205 """
    206 E:堡垒机eyeFortressackendsession_tracker.sh 复制到 CentOS-7 的 /home/ 目录下
    207 ————————SH脚本测试 开始————————
    208 [root@localhost ~]# sh /home/session_tracker.sh 
    209 [root@localhost ~]# sh /home/session_tracker.sh wybs123 99 
    210 ————————SH脚本测试 结束————————
    211 ————————用python3测试SH脚本 开始————————
    212 [root@localhost ~]# python3
    213 >>> import subprocess
    214 >>> subprocess.run("sshpass -p 123456 ssh root@192.192.168.110 -E wybs123 -o StrictHostKeyChecking=no",shell=True)
    215 ————————另启一个会话查看————————
    216 [root@localhost ~]# ps -ef | grep  wybs123 | grep ssh | grep -v sshpass | awk '{ print $2 }' 
    217 [root@localhost ~]# sh /home/session_tracker.sh wybs123 99 
    218 ————————用python3测试SH脚本 结束————————
    219 """#SH脚本测试
    220 """
    221 ————————创建跳板机的独立用户 开始————————
    222 [root@localhost ~]# useradd jump |#新建用户(跳)      
    223 [root@localhost ~]# passwd jump  |#修改用户(跳)的密码
    224 新的 密码:admineye
    225 重新输入新的 密码:admineye
    226 ————————创建跳板机的独立用户 结束————————
    227 ————————测试独立用户Django使用权限 开始————————
    228 [root@localhost ~] su jump
    229 [jump@localhost root]$ python3
    230 >>> import django
    231 >>> print(django.get_version())
    232 2.1.4
    233 >>> exit()
    234 [jump@localhost root]$ su root
    235 密码:123456
    236 ————————测试独立用户Django使用权限 结束————————
    237 ——虚拟机不用——————Luxin系统给独立用户Django使用权限 开始——site-packages——————
    238 [root@localhost ~]# python3
    239 >>> import sys
    240 >>> sys.path
    241 >>> exit()
    242 [root@localhost ~]# sudo cp -rp /usr/lib64/anacnda3/lib/python3.6/site-packages/* /home/jump/.local/lib/python3.6/site-packages/
    243 [root@localhost ~]# cd /home/jump/.local/lib/python3.6/site-packages/
    244 [root@localhost ~]# sudo chown -R jump.jump *
    245 [root@localhost ~]# ll
    246 ——虚拟机不用——————Luxin系统给独立用户Django使用权限 结束——site-packages——————
    247 """#独立用户
    248 """
    249 E:堡垒机eye 将项目复制到 CentOS-7 的 /home/jump/ 目录下
    250 ———————复制Python代码到服务用户目录下 开始—————————
    251 注意:修改settings中的设置,使其允许所有网段访问: ALLOWED_HOSTS = ['*']
    252 [root@localhost ~]# cd /home/jump/eye
    253 [root@localhost eye]# ls
    254 [root@localhost eye]# python3 manage.py runserver 0.0.0.0:9000 |#运行服务时使用0.0.0.0:9000作为IP和端口,执行命令
    255 [root@localhost ~]# systemctl stop firewalld.service |#注意:关闭防火墙 
    256 ———————复制Python代码到服务用户目录下 结束—————————
    257 
    258 ———————测试项目交互脚本 开始—————————
    259 [root@localhost ~]# python3 /home/jump/eye/fortess_manage.py
    260 [root@localhost ~]# chmod -R 777 /home/jump |#修改权限,复制python项目文件到Luxin系统后注意重新赋值权限
    261 ———————测试项目交互脚本 结束—————————
    262 """#启动服务
    263 """
    264 ———————Windows系统测试 开始—————————
    265 浏览器访问:http://192.192.168.143:9000/admin/
    266 ————putty测试 开始————
    267 login as: jump
    268 jump@192.192.168.143's password:admineye
    269 ----开始 运行交互脚本。。。----
    270 Username:admin@qq.com
    271 Password:admin123456
    272 ----------------------欢迎 admin-----------------------
    273 0.      测试组[1]
    274 z.      未分组主机[1]
    275 admin>>:
    276 ————putty测试 结束————
    277 ———————Windows系统测试 结束—————————
    278 """#Windows系统测试
    279 """
    280 ——————————session_tracker.sh给独立用户jump权限,创建日志免密码 开始——————————
    281 [root@localhost ~]# vim /etc/sudoers
    282 ———————进入vim文本编辑操作 开始—————————
    283 | i |#进入插入文本模式
    284 _______到底部插入 开始_______
    285 %jump  ALL=NOPASSWD:ALL #/use/bin/strace,/usr/bin/python3
    286 _______到底部插入 结束_______
    287 | Esc |#退出插入文本模式
    288 | Shift + Q  |#进行EX操作模式
    289 | wq! |#保存#退出vim文本编辑操作
    290 ———————进入vim文本编辑操作 结束—————————
    291 ——————————session_tracker.sh给独立用户jump权限,创建日志免密码 结束——————————
    292 """#创建日志
    293 """
    294 ————————配置独立用户启动项 开始————————
    295 [root@localhost ~]# cd /home/jump
    296 [root@localhost jump]# ls -a
    297 .  ..  .bash_logout  .bash_profile  .bashrc
    298 [root@localhost eye]# vim .bashrc
    299 ————如果vim打开错误 开始————
    300 [root@localhost conf]# rpm -qa|grep vim     |#如果vim打开错误
    301 [root@localhost conf]# yum -y install vim*  |#安装vim
    302 ————如果vim打开错误 结束————
    303 ————进入vim文本编辑操作 开始————
    304 | i |#进入插入文本模式
    305 _____原有的内容 开始_____
    306 if [ -f /etc/bashrc ]; then
    307         . /etc/bashrc
    308 fi
    309 _____原有的内容 结束_____
    310 _____插入的内容 开始_____
    311 python3 /home/jump/eye/fortess_manage.py
    312 logout
    313 _____插入的内容 结束_____
    314 | Esc |#退出插入文本模式
    315 | Shift + Q  |#进行EX操作模式
    316 | w |#保存vim文本编辑操作
    317 | q |#退出vim
    318 ————进入vim文本编辑操作 结束————
    319 ————————配置独立用户启动项 结束————————
    320 """#启动项
    321 """
    322 ———————复制Python代码,后快速启动服务 开始—————————
    323 [root@localhost ~]# chmod -R 777 /home/jump |#修改权限,复制python项目文件到Luxin系统后注意重新赋值权限
    324 [root@localhost ~]# python3 /home/jump/eye/manage.py runserver 0.0.0.0:9000
    325 ———————复制Python代码,后快速启动服务 结束—————————
    326 """#快速启动服务
    #main.py

      1 #models.py
      2 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
      3 from django.db import models  #模型
      4 from django.contrib.auth.models import BaseUserManager,AbstractBaseUser,PermissionsMixin#DJANGO账户继承重写
      5 #重写DJAGO账号表
      6 class UserProfileManager(BaseUserManager):
      7     def create_user(self, email, name, password=None):#创建并保存一个普通用户给定的邮箱地址,密码。
      8         if not email:
      9             raise ValueError('用户必须有一个电子邮箱地址')
     10         user = self.model(email=self.normalize_email(email),#验证邮箱格式
     11                           name=name,)#创建一条普通用户记录
     12         user.set_password(password)#对密码加密
     13         user.save(using=self._db)#从临时文件读取出来数据,进行保存。
     14         return user
     15     def create_superuser(self, email,name,password):#创建并保存一个超级用户给定的邮箱地址,密码。
     16         user = self.create_user(email,name=name,password=password)#创建一条超级用户记录
     17         user.is_admin = True#管理员
     18         user.save(using=self._db)#从临时文件读取出来数据,进行保存。
     19         return user
     20 #重写DJAGO账号表
     21 class UserProfile(AbstractBaseUser,PermissionsMixin):
     22     # ————#DJANGO账号必须要有的字段 开始————
     23     email = models.EmailField( max_length=255,unique=True,verbose_name='邮箱地址')
     24     name = models.CharField(max_length=32,verbose_name='昵称')
     25     is_active = models.BooleanField(default=True,verbose_name='是否启用')
     26     is_admin = models.BooleanField(default=False,verbose_name='管理员',help_text='指定用户是否可以登录管理网站并且拥有全部权限',)
     27     is_staff = models.BooleanField(default=True,verbose_name='员工状态',help_text='指定用户是否可以登录管理网站',)
     28     # ————#DJANGO账号必须要有的字段 结束————
     29 
     30     # ————————————————02堡垒机设计后台交互程序 开始————————————————
     31     bind_hosts = models.ManyToManyField('BindHost',blank=True,verbose_name="关联的主机")
     32     host_groups = models.ManyToManyField('HostGroups',blank=True,verbose_name="关联的分组")
     33     # ————————————————02堡垒机设计后台交互程序 结束————————————————
     34 
     35     #————#创建用户记录 开始————
     36     objects = UserProfileManager()#创建用户记录,固定的变量名(objects)
     37     USERNAME_FIELD = 'email' #用于登录的字段
     38     REQUIRED_FIELDS = ['name']#必须填写
     39     # ————#创建用户记录 结束————
     40     # ————#DJANGO账号必须要有的方法 开始————
     41     def get_full_name(self):#用户确认的电子邮件地址
     42         return self.email
     43     def get_short_name(self):#用户确认的电子邮件地址
     44         return self.email
     45     # ————#DJANGO账号必须要有的方法 结束————
     46     def __str__(self):# __unicode__ on Python 2
     47         return self.email
     48     # ————#DJANGO账号权限 开始————
     49     def has_perm(self, perm, obj=None):#权限#用户有一个特定的许可吗?
     50         return True
     51     def has_module_perms(self, app_label):#用户有权限查看应用名称吗?
     52         return True
     53     # ————#DJANGO账号权限 结束————
     54     class Meta:
     55         verbose_name_plural='00账户'
     56 # ————自定制admin 开始————
     57 #到admin.py里给DJANGO账户注册显示 #E:堡垒机eyeFortressadmin.py
     58 # ————自定制admin 结束————
     59 # ————注册到配置文件 开始————
     60 #到配置文件里给DJANGO账户指定模型 #E:堡垒机eyeeyesettings.py
     61 #AUTH_USER_MODEL = 'web.UserProfile'
     62 # ————注册到配置文件 结束————
     63 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
     64 #————————————————02堡垒机设计后台交互程序 开始————————————————
     65 #01标签
     66 class Tag(models.Model):
     67     name =  models.CharField(max_length=64,unique=True) #标签名#CharField定长文本#最长度=64字节#不可以重复
     68     class Meta:#通过一个内嵌类 "class Meta" 给你的 model 定义元数据
     69         verbose_name_plural =  "01标签" #verbose_name_plural给你的模型类起一个更可读的名字
     70     def __str__(self):  # __str__()是Python的一个“魔幻”方法,这个方法定义了当object调用str()时应该返回的值。
     71         return self.name  # 返回 #标签名
     72 #02机房
     73 class IDC(models.Model):
     74     name = models.CharField(max_length=64, unique=True,verbose_name="机房名")
     75     address=models.CharField(max_length=256,blank=True,null=True,verbose_name="机房地址")
     76     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="机房的备注")
     77     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
     78     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
     79     class Meta:
     80         verbose_name_plural='02机房'
     81     def __str__(self):
     82         return self.name
     83 #03主机
     84 class Host(models.Model):
     85     hostname = models.CharField(max_length=64,unique=True,verbose_name="主机名")
     86     ip_addr = models.GenericIPAddressField(verbose_name="主机IP地址")
     87     port = models.SmallIntegerField(default=22,verbose_name="主机端口")
     88     idc = models.ForeignKey('IDC',blank=True,null=True,on_delete=models.CASCADE,verbose_name="主机所在的机房")
     89     system_type_choices = ((0,'Linux'),(1,'Windows'))
     90     system_type = models.SmallIntegerField(choices=system_type_choices,default=0,verbose_name="操作系统类型")
     91     enabled = models.BooleanField(default=1,verbose_name="是否启用本机")
     92     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="主机的备注")
     93     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
     94     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
     95     class Meta:
     96         verbose_name_plural='03主机'
     97         unique_together = ('ip_addr','port')
     98     def __str__(self):
     99         return "%s(%s)" % (self.hostname, self.ip_addr)
    100 #04账号密码
    101 class RemoteUser(models.Model):
    102     auth_type_choices =((0,'ssh-password'), (1,'ssh-key'))
    103     auth_type = models.SmallIntegerField(choices=auth_type_choices,default=0,verbose_name="连接方式,密码或者密钥")
    104     userlevel=models.CharField(max_length=128,blank=True,null=True,verbose_name="用户等级")
    105     username = models.CharField(max_length=128,verbose_name="用户名")
    106     password = models.CharField(max_length=256,verbose_name="密码或者密钥",help_text="如果连接方式选择为ssh-key,那此处就应该是key的路径")
    107     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="账号密码的备注")
    108     host_prompt=models.ManyToManyField("Host",blank=True,verbose_name="可关联的主机",help_text="关联主机与账号密码提示")
    109     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    110     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    111     class Meta:
    112         verbose_name_plural='04账号密码'
    113         unique_together = ('auth_type', 'username', 'password')  # 联合唯一
    114     def __str__(self):
    115         return "%s:%s:%s" %(self.id,self.username,self.memo)
    116 #05关联主机与账号密码
    117 class BindHost(models.Model):
    118     host = models.ForeignKey('Host',on_delete=models.CASCADE,verbose_name="远程主机名")
    119     remote_user = models.ForeignKey('RemoteUser',on_delete=models.CASCADE,verbose_name="远程用户名")
    120     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="关联的备注")
    121     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    122     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    123     class Meta:
    124         verbose_name_plural = '05关联主机与账号密码'
    125         unique_together = ('host', 'remote_user')#联合唯一
    126     def __str__(self):
    127         return "<%s:%s:%s>" %(self.host.id,self.host.hostname,self.remote_user.username)
    128 #06主机分组
    129 class HostGroups(models.Model):
    130     name  = models.CharField(max_length=64,unique=True,verbose_name="分组名称")
    131     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="主机分组的备注")
    132     bind_hosts = models.ManyToManyField('BindHost',blank=True,verbose_name="远程主机")
    133     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    134     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    135     class Meta:
    136         verbose_name_plural='06主机分组'
    137     def __str__(self):
    138         return self.name
    139 #————————————————02堡垒机设计后台交互程序 结束————————————————
    140 #————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
    141 #07会话日志
    142 class Session(models.Model):
    143     user = models.ForeignKey('UserProfile',on_delete=models.CASCADE,verbose_name='堡垒机账号')
    144     bind_host = models.ForeignKey('BindHost',on_delete=models.CASCADE,verbose_name='远程主机')
    145     uuid = models.CharField(max_length=128,default='n/a',verbose_name='唯一标识,进程ID')
    146     cmd_count = models.IntegerField(default=0,verbose_name='命令执行数量')
    147     stay_time = models.IntegerField(default=0,verbose_name="停留时长(秒)" ,help_text="每次刷新自动计算停留时间")
    148     closed = models.BooleanField(default=False,verbose_name='是否关闭这条会话记录')
    149     memo = models.CharField(max_length=128,blank=True,null=True,verbose_name="会话日志的备注")
    150     tag = models.ManyToManyField("Tag",blank=True,verbose_name="标签")#多对多关联到 标签表
    151     date = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    152     def __str__(self):
    153         return '<id:%s user:%s bind_host:%s>' % (self.id,self.user.email,self.bind_host.host)
    154     class Meta:
    155         verbose_name_plural = '07会话日志'
    156 #————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
    157 
    158 """
    159 # ————————————————Django中使用中国时区和中文 开始————————————————
    160 到settings.py里修改#E:堡垒机eyeeyesettings.py
    161 LANGUAGE_CODE = 'zh-Hans'
    162 TIME_ZONE = 'Asia/Shanghai'
    163 如果需要全局配置默认时区和语言,可在django下的conf模块下的global_settings.py中进行配置
    164 所有可选的语言选项可通过python输入以下代码查看:
    165 from django.conf.locale import LANG_INFO
    166 {key: LANG_INFO[key]['name_local'] for key in LANG_INFO if 'name_local' in LANG_INFO[key]}
    167 # ————————时间扩展 开始———————— 
    168 USE_TZ = True
    169 当设置了 USE_TZ 为 True 时,Django 与其他系统或服务的交流将强制使用 UTC 时间。
    170 1.保证存储到数据库中的是 UTC 时间;
    171 2.在函数之间传递时间参数时,确保时间已经转换成 UTC 时间;
    172 # ——Django中跨时区 开始——
    173 一般不跨时区的应用,可以不使用时区,即在settings.py设置
    174 USE_TZ = False
    175 这样存储的时间就是无时区的时间。
    176 # ——Django中跨时区 结束——
    177 # ————————时间扩展 结束————————
    178 # ————————————————Django中使用中国时区和中文 结束————————————————
    179 """#Django中使用中国时区和中文
    180 """
    181 # ————————————————Django数据库生成 开始————————————————
    182 运行 Terminal
    183 生成 数据表
    184 E:堡垒机eye>python manage.py makemigrations
    185 数据表 迁移
    186 E:堡垒机eye>python manage.py migrate
    187 # ————————————————Django数据库生成 结束————————————————
    188 """#Django数据库生成
    189 """
    190 # ————————————————重写Django账号表后的创建方法 开始————————————————
    191 E:堡垒机eye>python manage.py createsuperuser
    192 邮箱地址: admin@qq.com
    193 昵称: admin
    194 Password:           admin123456
    195 Password (again):   admin123456    
    196 Superuser created successfully.
    197 # ————————————————重写Django账号表后的创建方法 结束————————————————
    198 #启动 DJANGO 服务
    199 E:堡垒机eye>python manage.py runserver 127.0.0.1:8000
    200 """#重写Django账号表后的创建方法
    #models.py

      1 #admin.py
      2 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
      3 #————————同目录下可以用 . 开始————————
      4 #from Fortress import models#数据库 #重写DJAGO账号表
      5 from . import models#数据库 #重写DJAGO账号表
      6 #————————同目录下可以用 . 结束————————
      7 from django import forms #django的字段验证
      8 from django.contrib.auth.admin import UserAdmin as BaseUserAdmin ##表单添加和更改用户实例
      9 from django.contrib.auth.forms import ReadOnlyPasswordHashField#只读密码散列字段
     10 from django.contrib import admin #注册类到admin
     11 #创建新用户
     12 class UserCreationForm(forms.ModelForm):
     13     password1 = forms.CharField(label='设置密码', widget=forms.PasswordInput)#密码
     14     password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)#确认密码
     15     class Meta:
     16         model = models.UserProfile
     17         fields = ['email', 'name']
     18     def clean_password2(self): #检查两个密码条目是否一样(匹配)
     19         password1 = self.cleaned_data.get("password1")
     20         password2 = self.cleaned_data.get("password2")
     21         if password1 and password2 and password1 != password2:
     22             raise forms.ValidationError("密码不匹配")
     23         return password2
     24     def save(self, commit=True): #保存密码(散列的格式提供)
     25         user = super(UserCreationForm, self).save(commit=False)
     26         user.set_password(self.cleaned_data["password1"])
     27         if commit:
     28             user.save()#保存
     29         return user
     30 # 修改用户密码
     31 class UserChangeForm(forms.ModelForm):
     32     password = ReadOnlyPasswordHashField(label="修改密码",
     33         help_text=("原始密码不存储,所以没有办法看到"
     34                     "这个用户的密码,但是你可以改变密码 "
     35                     "使用 <a href="../password/">修改密码</a>."))#哈值#只读密码散列字段
     36     class Meta:
     37         model = models.UserProfile
     38         fields = ['email', 'password', 'name', 'is_active', 'is_admin']
     39     def clean_password(self):#不管用户提供什么,返回初始值。
     40         return self.initial["password"]
     41 
     42 #表单添加和更改用户实例
     43 class UserProfileAdmin(BaseUserAdmin):
     44     form = UserChangeForm  #修改
     45     add_form = UserCreationForm#创建
     46     # 字段用于显示用户模型。
     47     # 这些覆盖定义UserAdmin固定在底座上
     48     # auth.User参考特定字段。
     49     list_display = ['email', 'name','is_staff', 'is_admin']#后台显示的字段
     50     list_filter = ['is_admin','is_staff']#后台过滤的字段
     51     fieldsets = (('账号密码', {'fields': ('email', 'password')}),
     52                  ('个人信息', {'fields': ('name',)}),
     53                  # ————————————————02堡垒机设计后台交互程序 开始————————————————
     54                  ('堡垒机主机授权', {'fields': ('bind_hosts', 'host_groups')}),
     55                  # ————————————————02堡垒机设计后台交互程序 结束————————————————
     56                  ('权限', {'fields': ('is_admin','is_staff','user_permissions','groups')}),)
     57     #添加账户时显示要填写的字段
     58     add_fieldsets = (('添加账户', {'classes': ('wide',),#wide(宽屏样式)
     59             'fields': ('email', 'name', 'password1', 'password2')}),)
     60     search_fields = ['email']#后台搜索的字段
     61     ordering = ['email']#后台排序的字段
     62     filter_horizontal =['user_permissions','groups',#必须覆盖的变量#后台复选框的字段
     63                         # ————————————————02堡垒机设计后台交互程序 开始————————————————
     64                         'bind_hosts', 'host_groups',
     65                         # ————————————————02堡垒机设计后台交互程序 结束————————————————
     66                         ]
     67 admin.site.register(models.UserProfile, UserProfileAdmin)#现在注册这个新UserProfileAdmin
     68 # ——可选——使用Django的内置权限 开始————
     69 #from django.contrib.auth.models import Group #权限组
     70 #admin.site.unregister(Group)#权限组#因为我们不使用Django的内置权限,
     71 # ——可选——使用Django的内置权限 结束————
     72 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
     73 
     74 
     75 #————————————————02堡垒机设计后台交互程序 开始————————————————
     76 #01标签
     77 class TagAdmin(admin.ModelAdmin):
     78     list_display = ['id','name'] #显示字段表头
     79     list_editable = ['name']  #可编辑
     80     search_fields = ['name'] #搜索
     81 admin.site.register(models.Tag, TagAdmin)
     82 #02机房
     83 class IDCAdmin(admin.ModelAdmin):
     84     list_display = ['id','name','address','memo','date'] #显示字段表头
     85     list_editable = ['name','address', 'memo']  #可编辑
     86     search_fields = ['name','tag'] #搜索
     87     filter_horizontal = ['tag'] #复选框
     88 admin.site.register(models.IDC, IDCAdmin)
     89 #03主机
     90 class HostAdmin(admin.ModelAdmin):
     91     list_display = ['id','hostname','ip_addr','port','memo','enabled','date'] #显示字段表头
     92     list_editable = ['hostname','ip_addr','port','memo','enabled']  #可编辑
     93     search_fields = ['hostname','ip_addr','port','tag'] #搜索
     94     filter_horizontal = ['tag'] #复选框
     95 admin.site.register(models.Host, HostAdmin)
     96 #04账号密码
     97 class RemoteUserAdmin(admin.ModelAdmin):
     98     list_display = ['id','userlevel','username','memo','date'] #显示字段表头
     99     list_editable = ['memo']  #可编辑
    100     search_fields = ['userlevel','username','tag'] #搜索
    101     filter_horizontal = ['host_prompt','tag'] #复选框
    102 admin.site.register(models.RemoteUser,RemoteUserAdmin)
    103 #05关联主机与账号密码
    104 class BindHostAdmin(admin.ModelAdmin):
    105     list_display = ['id','host','remote_user','memo','date']#显示字段表头
    106     list_editable = ['host','remote_user','memo']  #可编辑
    107     search_fields = ['host','remote_user','tag'] #搜索
    108     filter_horizontal = ['tag'] #复选框
    109     ordering = ['host','remote_user'] #自定义排序
    110 admin.site.register(models.BindHost,BindHostAdmin)
    111 #06主机分组
    112 class HostGroupsAdmin(admin.ModelAdmin):
    113     list_display = ['id','name','memo','date']#显示字段表头
    114     list_editable = ['name','memo']  #可编辑
    115     search_fields = ['name','tag'] #搜索
    116     filter_horizontal = ['bind_hosts','tag'] #复选框
    117 admin.site.register(models.HostGroups, HostGroupsAdmin)
    118 #————————————————02堡垒机设计后台交互程序 结束————————————————
    119 # ————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
    120 #07会话日志
    121 class SessionAdmin(admin.ModelAdmin):
    122     list_display = ['id','user','bind_host','cmd_count','stay_time','closed','memo','date']#显示字段表头
    123     list_editable = ['closed','memo']  #可编辑
    124     search_fields = ['user','bind_host','memo','tag'] #搜索
    125     filter_horizontal = ['tag'] #复选框
    126 admin.site.register(models.Session, SessionAdmin)
    127 # ————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
    #admin.py

      1 #settings.py
      2 
      3 """
      4 Django settings for eye project.
      5 
      6 Generated by 'django-admin startproject' using Django 2.1.4.
      7 
      8 For more information on this file, see
      9 https://docs.djangoproject.com/en/2.1/topics/settings/
     10 
     11 For the full list of settings and their values, see
     12 https://docs.djangoproject.com/en/2.1/ref/settings/
     13 """
     14 
     15 import os
     16 
     17 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
     18 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
     19 
     20 
     21 # Quick-start development settings - unsuitable for production
     22 # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
     23 
     24 # SECURITY WARNING: keep the secret key used in production secret!
     25 SECRET_KEY = 'r35s$()7lli1hw-!6%llp0uu$!m!e#nnlxu+&!6$(g*6f0=@2#'
     26 
     27 # SECURITY WARNING: don't run with debug turned on in production!
     28 DEBUG = True
     29 # ————————————————03堡垒机在Linux系统里记录会话日志 开始————————————————
     30 # ALLOWED_HOSTS = []
     31 ALLOWED_HOSTS = ['*']
     32 # ————————————————03堡垒机在Linux系统里记录会话日志 结束————————————————
     33 # Application definition
     34 
     35 INSTALLED_APPS = [
     36     'django.contrib.admin',
     37     'django.contrib.auth',
     38     'django.contrib.contenttypes',
     39     'django.contrib.sessions',
     40     'django.contrib.messages',
     41     'django.contrib.staticfiles',
     42     'Fortress.apps.FortressConfig',
     43 ]
     44 
     45 MIDDLEWARE = [
     46     'django.middleware.security.SecurityMiddleware',
     47     'django.contrib.sessions.middleware.SessionMiddleware',
     48     'django.middleware.common.CommonMiddleware',
     49     'django.middleware.csrf.CsrfViewMiddleware',
     50     'django.contrib.auth.middleware.AuthenticationMiddleware',
     51     'django.contrib.messages.middleware.MessageMiddleware',
     52     'django.middleware.clickjacking.XFrameOptionsMiddleware',
     53 ]
     54 
     55 ROOT_URLCONF = 'eye.urls'
     56 
     57 TEMPLATES = [
     58     {
     59         'BACKEND': 'django.template.backends.django.DjangoTemplates',
     60         'DIRS': [os.path.join(BASE_DIR, 'templates')]
     61         ,
     62         'APP_DIRS': True,
     63         'OPTIONS': {
     64             'context_processors': [
     65                 'django.template.context_processors.debug',
     66                 'django.template.context_processors.request',
     67                 'django.contrib.auth.context_processors.auth',
     68                 'django.contrib.messages.context_processors.messages',
     69             ],
     70         },
     71     },
     72 ]
     73 
     74 WSGI_APPLICATION = 'eye.wsgi.application'
     75 
     76 
     77 # Database
     78 # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
     79 
     80 DATABASES = {
     81     'default': {
     82         'ENGINE': 'django.db.backends.sqlite3',
     83         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
     84     }
     85 }
     86 
     87 
     88 # Password validation
     89 # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
     90 
     91 AUTH_PASSWORD_VALIDATORS = [
     92     {
     93         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
     94     },
     95     {
     96         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
     97     },
     98     {
     99         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    100     },
    101     {
    102         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    103     },
    104 ]
    105 
    106 
    107 # Internationalization
    108 # https://docs.djangoproject.com/en/2.1/topics/i18n/
    109 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
    110 # ————Django中使用中国时区和中文 开始————
    111 #LANGUAGE_CODE = 'en-us'
    112 #TIME_ZONE = 'UTC'
    113 LANGUAGE_CODE = 'zh-Hans'
    114 TIME_ZONE = 'Asia/Shanghai'
    115 # ————Django中使用中国时区和中文 结束————
    116 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
    117 USE_I18N = True
    118 
    119 USE_L10N = True
    120 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
    121 # ————Django中跨时区 开始————
    122 # USE_TZ = True
    123 USE_TZ = False
    124 # ————Django中跨时区 结束————
    125 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
    126 # Static files (CSS, JavaScript, Images)
    127 # https://docs.djangoproject.com/en/2.1/howto/static-files/
    128 
    129 STATIC_URL = '/static/'
    130 
    131 #————————————————01堡垒机重写DJANGO账户表 开始————————————————
    132 AUTH_USER_MODEL = 'Fortress.UserProfile'
    133 #————————————————01堡垒机重写DJANGO账户表 结束————————————————
    #settings.py

     1 """
     2 ————————创建跳板机的独立用户 开始————————
     3 [root@localhost ~]# useradd jump |#新建用户(跳)      
     4 [root@localhost ~]# passwd jump  |#修改用户(跳)的密码
     5 新的 密码:admineye
     6 重新输入新的 密码:admineye
     7 ————————创建跳板机的独立用户 结束————————
     8 ————————测试独立用户Django使用权限 开始————————
     9 [root@localhost ~] su jump
    10 [jump@localhost root]$ python3
    11 >>> import django
    12 >>> print(django.get_version())
    13 2.1.4
    14 >>> exit()
    15 [jump@localhost root]$ su root
    16 密码:123456
    17 ————————测试独立用户Django使用权限 结束————————
    18 ——虚拟机不用——————Luxin系统给独立用户Django使用权限 开始——site-packages——————
    19 [root@localhost ~]# python3
    20 >>> import sys
    21 >>> sys.path
    22 >>> exit()
    23 [root@localhost ~]# sudo cp -rp /usr/lib64/anacnda3/lib/python3.6/site-packages/* /home/jump/.local/lib/python3.6/site-packages/
    24 [root@localhost ~]# cd /home/jump/.local/lib/python3.6/site-packages/
    25 [root@localhost ~]# sudo chown -R jump.jump *
    26 [root@localhost ~]# ll
    27 ——虚拟机不用——————Luxin系统给独立用户Django使用权限 结束——site-packages——————
    28 """#独立用户
    #独立用户

     1 """
     2 E:堡垒机eye 将项目复制到 CentOS-7 的 /home/jump/ 目录下
     3 ———————复制Python代码到服务用户目录下 开始—————————
     4 注意:修改settings中的设置,使其允许所有网段访问: ALLOWED_HOSTS = ['*']
     5 [root@localhost ~]# cd /home/jump/eye
     6 [root@localhost eye]# ls
     7 [root@localhost eye]# python3 manage.py runserver 0.0.0.0:9000 |#运行服务时使用0.0.0.0:9000作为IP和端口,执行命令
     8 [root@localhost ~]# systemctl stop firewalld.service |#注意:关闭防火墙 
     9 ———————复制Python代码到服务用户目录下 结束—————————
    10 
    11 ———————测试项目交互脚本 开始—————————
    12 [root@localhost ~]# python3 /home/jump/eye/fortess_manage.py
    13 [root@localhost ~]# chmod -R 777 /home/jump |#修改权限,复制python项目文件到Luxin系统后注意重新赋值权限
    14 ———————测试项目交互脚本 结束—————————
    15 """#启动服务
    #启动服务

     1 """
     2 ———————Windows系统测试 开始—————————
     3 浏览器访问:http://192.192.168.143:9000/admin/
     4 ————putty测试 开始————
     5 login as: jump
     6 jump@192.192.168.143's password:admineye
     7 ----开始 运行交互脚本。。。----
     8 Username:admin@qq.com
     9 Password:admin123456
    10 ----------------------欢迎 admin-----------------------
    11 0.      测试组[1]
    12 z.      未分组主机[1]
    13 admin>>:
    14 ————putty测试 结束————
    15 ———————Windows系统测试 结束—————————
    16 """#Windows系统测试
    #Windows系统测试


     1 """
     2 ——————————session_tracker.sh给独立用户jump权限,创建日志免密码 开始——————————
     3 [root@localhost ~]# vim /etc/sudoers
     4 ———————进入vim文本编辑操作 开始—————————
     5 | i |#进入插入文本模式
     6 _______到底部插入 开始_______
     7 %jump  ALL=NOPASSWD:ALL #/use/bin/strace,/usr/bin/python3
     8 _______到底部插入 结束_______
     9 | Esc |#退出插入文本模式
    10 | Shift + Q  |#进行EX操作模式
    11 | wq! |#保存#退出vim文本编辑操作
    12 ———————进入vim文本编辑操作 结束—————————
    13 ——————————session_tracker.sh给独立用户jump权限,创建日志免密码 结束——————————
    14 """#创建日志
    #创建日志


     1 """
     2 ————————配置独立用户启动项 开始————————
     3 [root@localhost ~]# cd /home/jump
     4 [root@localhost jump]# ls -a
     5 .  ..  .bash_logout  .bash_profile  .bashrc
     6 [root@localhost eye]# vim .bashrc
     7 ————如果vim打开错误 开始————
     8 [root@localhost conf]# rpm -qa|grep vim     |#如果vim打开错误
     9 [root@localhost conf]# yum -y install vim*  |#安装vim
    10 ————如果vim打开错误 结束————
    11 ————进入vim文本编辑操作 开始————
    12 | i |#进入插入文本模式
    13 _____原有的内容 开始_____
    14 if [ -f /etc/bashrc ]; then
    15         . /etc/bashrc
    16 fi
    17 _____原有的内容 结束_____
    18 _____插入的内容 开始_____
    19 python3 /home/jump/eye/fortess_manage.py
    20 logout
    21 _____插入的内容 结束_____
    22 | Esc |#退出插入文本模式
    23 | Shift + Q  |#进行EX操作模式
    24 | w |#保存vim文本编辑操作
    25 | q |#退出vim
    26 ————进入vim文本编辑操作 结束————
    27 ————————配置独立用户启动项 结束————————
    28 """#启动项
    #启动项

  • 相关阅读:
    d is less efficient than [0-9]
    How to navigate back to the last cursor position in Visual Studio Code?
    Is there a short-hand for nth root of x in Python 开n次方
    Disable source maps in Chrome DevTools
    Disable map files on SASS
    快速理解脏读,不可重复读,幻读
    AWR学习
    oracle set命令详解
    TimescaleDB上手和性能测试
    Centos 7.5 通过yum安装GNOME Desktop时出现:file /boot/efi/EFI/centos from install of fwupdate-efi-12-5.el7.centos.x86_64 conflicts with file from package grub2-common-1:2.02-0.65.el7.centos.2.noarch
  • 原文地址:https://www.cnblogs.com/ujq3/p/10162411.html
Copyright © 2011-2022 走看看