mirror of https://github.com/openspug/spug
U api add host/setting app
parent
0b36aaf6c1
commit
bad6431553
|
@ -0,0 +1,28 @@
|
|||
from django.db import models
|
||||
from libs import ModelMixin, human_time
|
||||
from apps.account.models import User
|
||||
from libs.ssh import SSH
|
||||
|
||||
|
||||
class Host(models.Model, ModelMixin):
|
||||
name = models.CharField(max_length=50)
|
||||
zone = models.CharField(max_length=50)
|
||||
hostname = models.CharField(max_length=50)
|
||||
port = models.IntegerField()
|
||||
username = models.CharField(max_length=50)
|
||||
desc = models.CharField(max_length=255, null=True)
|
||||
|
||||
created_at = models.CharField(max_length=20, default=human_time)
|
||||
created_by = models.ForeignKey(User, models.PROTECT, related_name='+')
|
||||
deleted_at = models.CharField(max_length=20, null=True)
|
||||
deleted_by = models.ForeignKey(User, models.PROTECT, related_name='+', null=True)
|
||||
|
||||
def get_ssh(self, pkey):
|
||||
return SSH(self.hostname, self.port, self.username, pkey)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Host %r>' % self.name
|
||||
|
||||
class Meta:
|
||||
db_table = 'hosts'
|
||||
ordering = ('-id',)
|
|
@ -0,0 +1,7 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import *
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', HostView.as_view()),
|
||||
]
|
|
@ -0,0 +1,70 @@
|
|||
from django.views.generic import View
|
||||
from libs import json_response, JsonParser, Argument
|
||||
from apps.setting.utils import AppSetting
|
||||
from apps.host.models import Host
|
||||
from libs.ssh import SSH, AuthenticationException
|
||||
from libs import human_time
|
||||
|
||||
|
||||
class HostView(View):
|
||||
def get(self, request):
|
||||
hosts = Host.objects.filter(deleted_by_id__isnull=True)
|
||||
return json_response(hosts)
|
||||
|
||||
def post(self, request):
|
||||
form, error = JsonParser(
|
||||
Argument('id', type=int, required=False),
|
||||
Argument('zone', help='请输入主机类型'),
|
||||
Argument('name', help='请输主机名称'),
|
||||
Argument('username', help='请输入登录用户名'),
|
||||
Argument('hostname', help='请输入主机名或IP'),
|
||||
Argument('port', type=int, help='请输入SSH端口'),
|
||||
Argument('desc', required=False),
|
||||
Argument('password', required=False),
|
||||
).parse(request.body)
|
||||
if error is None:
|
||||
if valid_ssh(form.hostname, form.port, form.username, form.pop('password')) is False:
|
||||
return json_response('auth fail')
|
||||
|
||||
if form.id:
|
||||
Host.objects.filter(pk=form.pop('id')).update(**form)
|
||||
else:
|
||||
form.created_by = request.user
|
||||
Host.objects.create(**form)
|
||||
return json_response(error=error)
|
||||
|
||||
def delete(self, request):
|
||||
form, error = JsonParser(
|
||||
Argument('id', type=int, help='请指定操作对象')
|
||||
).parse(request.GET)
|
||||
if error is None:
|
||||
Host.objects.filter(pk=form.id).update(
|
||||
deleted_at=human_time(),
|
||||
deleted_by=request.user,
|
||||
)
|
||||
return json_response(error=error)
|
||||
|
||||
|
||||
def valid_ssh(hostname, port, username, password):
|
||||
try:
|
||||
private_key = AppSetting.get('private_key')
|
||||
public_key = AppSetting.get('public_key')
|
||||
except KeyError:
|
||||
private_key, public_key = SSH.generate_key()
|
||||
AppSetting.set('private_key', private_key, 'ssh private key')
|
||||
AppSetting.set('public_key', public_key, 'ssh public key')
|
||||
if password:
|
||||
cli = SSH(hostname, port, username, password=password)
|
||||
code, stdout, stderr = cli.exec_command('mkdir -p -m 700 ~/.ssh && \
|
||||
echo %r >> ~/.ssh/authorized_keys && \
|
||||
chmod 600 ~/.ssh/authorized_keys' % public_key)
|
||||
if code != 0:
|
||||
raise Exception('add public key error: ' + ''.join(x for x in stderr))
|
||||
else:
|
||||
cli = SSH(hostname, port, username, private_key)
|
||||
|
||||
try:
|
||||
cli.ping()
|
||||
except AuthenticationException:
|
||||
return False
|
||||
return True
|
|
@ -0,0 +1,13 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Setting(models.Model):
|
||||
key = models.CharField(max_length=50, unique=True)
|
||||
value = models.TextField()
|
||||
desc = models.CharField(max_length=255, null=True)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Setting %r>' % self.key
|
||||
|
||||
class Meta:
|
||||
db_table = 'settings'
|
|
@ -0,0 +1,21 @@
|
|||
from functools import lru_cache
|
||||
from apps.setting.models import Setting
|
||||
|
||||
|
||||
class AppSetting:
|
||||
keys = ('public_key', 'private_key')
|
||||
|
||||
@classmethod
|
||||
@lru_cache(maxsize=64)
|
||||
def get(cls, key):
|
||||
info = Setting.objects.filter(key=key).first()
|
||||
if not info:
|
||||
raise KeyError(f'no such key for {key!r}')
|
||||
return info.value
|
||||
|
||||
@classmethod
|
||||
def set(cls, key, value, desc=None):
|
||||
if key in cls.keys:
|
||||
Setting.objects.update_or_create(key=key, defaults={'value': value, 'desc': desc})
|
||||
else:
|
||||
raise KeyError('invalid key')
|
|
@ -1,20 +1,21 @@
|
|||
from paramiko.client import SSHClient, AutoAddPolicy
|
||||
from paramiko.config import SSH_PORT
|
||||
from paramiko.rsakey import RSAKey
|
||||
from paramiko.ssh_exception import AuthenticationException
|
||||
from io import StringIO
|
||||
|
||||
|
||||
class SSH:
|
||||
def __init__(self, host, port=SSH_PORT, username='root', pkey=None, password=None, connect_timeout=10):
|
||||
def __init__(self, hostname, port=SSH_PORT, username='root', pkey=None, password=None, connect_timeout=10):
|
||||
if pkey is None and password is None:
|
||||
raise Exception('public key and password must have one is not None')
|
||||
self.client = None
|
||||
self.arguments = {
|
||||
'hostname': host,
|
||||
'hostname': hostname,
|
||||
'port': port,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'pkey': pkey,
|
||||
'pkey': RSAKey.from_private_key(StringIO(pkey)) if isinstance(pkey, str) else pkey,
|
||||
'timeout': connect_timeout,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Django==2.2.7
|
||||
channels==2.3.1
|
||||
channels==2.3.1
|
||||
paramiko==2.6.0
|
|
@ -31,6 +31,8 @@ ALLOWED_HOSTS = []
|
|||
INSTALLED_APPS = [
|
||||
'channels',
|
||||
'apps.account',
|
||||
'apps.host',
|
||||
'apps.setting',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
|
@ -16,5 +16,6 @@ Including another URLconf
|
|||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('account/', include('apps.account.urls'))
|
||||
path('account/', include('apps.account.urls')),
|
||||
path('host/', include('apps.host.urls')),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue