在Django里插入paramiko实现批量操作

背景交代

Django:2.1.1
paramiko:2.4.1
Python:3.6.5

paramiko在“python批量操作”范围内占据着龙头老大的的地位,它主要就是通过IP、端口和密码登录到对应的服务器执行具体的命令。在Django页面里,我们有时候需要批量操作资产去执行同一个命令,这个时候就可以把paramiko接入到django里。

首先先pip install paramiko,然后在app所在的文件夹里的models.py里添加新的数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django.db import models

# Create your models here.
#这里是服务器数据
class server(models.Model):
name = models.CharField(verbose_name='服务器名称',max_length=50)
inIP = models.GenericIPAddressField(verbose_name='服务器内网IP地址')
outIP = models.GenericIPAddressField(verbose_name='服务器外网IP地址',default='0.0.0.0')
port = models.IntegerField(verbose_name='登录端口',default='这里是SSH端口')
username = models.CharField(verbose_name='登录用户名',max_length=50,default='这里是登录用户名')
password = models.CharField(verbose_name='登录密码',max_length=100,default='这里是服务器密码')
signtime = models.DateField(auto_now_add=True)
remark = models.CharField(verbose_name='甲方环境',max_length=255)

def __unicode__(self):
return self.name

返回到manage.py这一层的目录,执行python manage.py makemigrationspython manage.py migrate,这个server表就是用来存储资产资料的。

整个paramiko执行的逻辑是这样的:首先在paramiko.html里展示所有的资产信息,同时页面有一个button按钮,点击这个button,后台开始对页面里所有的服务器执行同样的命令(这里举例执行date命令),将执行的结果生成一个新的页面叫result.html,页面跳转到result.html给用户展示。

具体配置

有了上面的铺垫,现在说一下paramiko在django里的具体配置。首先先在app所在的文件夹下新建一个paramiko_client.py的文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time,paramiko
from .models import server #注意这里的.

class ParamikoClient:
def __init__(self):
self.client = paramiko.SSHClient() #创建sshclient对象
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #允许将信任的主机自动加入到host_allow 列表,此方法必须放在connect方法的前面
self.sftp_client = None
self.client_state = 0

def connect(self,sshinfo):
try:
self.client.connect(hostname=sshinfo.outIP,port=sshinfo.port,username=sshinfo.username,password=sshinfo.password,timeout=1.0) #调用connect方法连接服务器
self.client_state = 1
return True
except Exception as e:
print (e)
try:
self.client.close()
except:
pass

def run_cmd(self,cmd_str):
stdin,stdout,stderr = self.client.exec_command(cmd_str)
return stdout.read()

然后在views.py里增加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import paramiko
from .paramiko_client import ParamikoClient #引用上面那个文件里的ParamikoClient函数

#批量操作
@login_required
def paramiko(request):
servers = server.objects.all() #获取server这个表里所有的资产信息
return render(request, 'agparamiko.html',{'servers':servers}) #反馈页面

#批量操作结果
def run_ssh_cmd(request):
sshs = server.objects.all().filter(inIP='192.168.1.1') #这里我就拿出来内网IP是192.168.1.1这个机器的例子
cmd_res = {} #设定一个空的列表
for ssh in sshs:
client = ParamikoClient()
client.connect(ssh)
res = client.run_cmd('date') #执行的命令是date
cmd_res[ssh.name] = res #给列表的元素一一对应赋值

return render(request, 'result.html',{'cmd_res':cmd_res}) #反馈界面

urls.py的对应内容如下:

1
2
3
4
5
6
7
from django.urls import path
from . import views

urlpatterns = [
path(r'paramiko.html',views.paramiko,name="paramiko"), #批量操作界面
path(r'result.html',views.run_ssh_cmd,name="run_ssh_cmd"), #批量操作界面
]

至于result.html就很简单了,主体部分代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="table-responsive">  <!-- 响应式表格 -->
<table id="server_table" class="table table-striped table-bordered table-hove">
<thead>
<tr>
<th>服务器名称</th>
<th>执行结果</th>
</tr>
</thead>
<tbody>
{% for key,value in cmd_res.items %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

保存之后,系统重新启动django,在页面执行的效果如下:
akb48

Django的model加密

上面已经实现了通过paramiko批量操作,但是在这个过程中,我们把密码明文的保存在mysql里,这样无疑是有安全隐患的。于是乎就有一个新的需求:当我们输入到mysql的时候是加密的,从mysql取值的时候是解密的,那么这样的需求可以实现么?当然可以,使用django-fernet-fields

首先要pip install django-fernet-fields安装这个插件,然后在对应的models.py里添加如下语句即可:

1
2
3
4
5
6
7
from fernet_fields import EncryptedTextField,EncryptedIntegerField

class MyModel(models.Model):
name = models.CharField(verbose_name='姓名',max_length=1000)
cardid = EncryptedTextField() #加密字段
phone = EncryptedIntegerField() #加密字段
address = models.CharField(verbose_name='住址',max_length=1000)

然后在manage.py同级文件夹里执行python manage.py makemigrationspython manage.py makemigrations,发现新的表已经生成,然后在admin.py里添加后台展示代码:

1
2
3
4
5
from .models import MyModel
class MyModelAdmin(admin.ModelAdmin):
list_display = ('name','cardid','phone','address')

admin.site.register(MyModel,MyModelAdmin)

然后登陆到django后台,发现一个叫My models的表已经生成了,那么我们添加一条记录如下:
akb48

跑到mysql命令行一看:
akb48

可见cardid和phone这两个字段已经被加密了,但是在views.py里使用.objects.values()方法获取是直接得到明文的,这样就达到了预期的效果。

补充一下,EncryptedIntegerField这个其实不太实用,它不能保存超过2147483647的数字,也就是说电话号码(11位)是无法用这个方法保存的…

参考资料

http://www.maiziedu.com/wiki/frame/embed/
https://django-fernet-fields.readthedocs.io/en/latest/
https://pypi.org/project/django-encrypted-model-fields/ 据说这个方法也能实现加密效果,我没有尝试
https://www.59izt.com/zhoubin/2019/04/10/7025.html
akb48

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------