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.client import SSHClient, AutoAddPolicy
|
||||||
from paramiko.config import SSH_PORT
|
from paramiko.config import SSH_PORT
|
||||||
from paramiko.rsakey import RSAKey
|
from paramiko.rsakey import RSAKey
|
||||||
|
from paramiko.ssh_exception import AuthenticationException
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
class SSH:
|
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:
|
if pkey is None and password is None:
|
||||||
raise Exception('public key and password must have one is not None')
|
raise Exception('public key and password must have one is not None')
|
||||||
self.client = None
|
self.client = None
|
||||||
self.arguments = {
|
self.arguments = {
|
||||||
'hostname': host,
|
'hostname': hostname,
|
||||||
'port': port,
|
'port': port,
|
||||||
'username': username,
|
'username': username,
|
||||||
'password': password,
|
'password': password,
|
||||||
'pkey': pkey,
|
'pkey': RSAKey.from_private_key(StringIO(pkey)) if isinstance(pkey, str) else pkey,
|
||||||
'timeout': connect_timeout,
|
'timeout': connect_timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
Django==2.2.7
|
Django==2.2.7
|
||||||
channels==2.3.1
|
channels==2.3.1
|
||||||
|
paramiko==2.6.0
|
|
@ -31,6 +31,8 @@ ALLOWED_HOSTS = []
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'channels',
|
'channels',
|
||||||
'apps.account',
|
'apps.account',
|
||||||
|
'apps.host',
|
||||||
|
'apps.setting',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
@ -16,5 +16,6 @@ Including another URLconf
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('account/', include('apps.account.urls'))
|
path('account/', include('apps.account.urls')),
|
||||||
|
path('host/', include('apps.host.urls')),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue