add judge_server related api

This commit is contained in:
virusdefender
2016-11-20 19:18:25 +08:00
parent 294fd8b5b2
commit f56d75cf91
9 changed files with 254 additions and 11 deletions

View File

@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.10 on 2016-11-19 14:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('conf', '0002_auto_20161119_1657'),
]
operations = [
migrations.CreateModel(
name='JudgeServer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hostname', models.CharField(max_length=64)),
('ip', models.CharField(max_length=32)),
('judger_version', models.CharField(max_length=24)),
('cpu_core', models.IntegerField()),
('memory_usage', models.FloatField()),
('cpu_usage', models.FloatField()),
('last_heartbeat', models.DateTimeField()),
('create_time', models.DateTimeField(auto_now_add=True)),
('task_number', models.IntegerField()),
],
options={
'db_table': 'judge_server',
},
),
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.10 on 2016-11-20 10:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('conf', '0003_judgeserver'),
]
operations = [
migrations.AddField(
model_name='judgeserver',
name='service_url',
field=models.CharField(blank=True, max_length=128, null=True),
),
migrations.AlterField(
model_name='judgeserver',
name='ip',
field=models.CharField(blank=True, max_length=32, null=True),
),
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.10 on 2016-11-20 11:00
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('conf', '0004_auto_20161120_1834'),
]
operations = [
migrations.CreateModel(
name='JudgeServerToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(max_length=32)),
],
options={
'db_table': 'judge_server_token',
},
),
]

View File

@@ -28,3 +28,25 @@ class WebsiteConfig(models.Model):
class Meta: class Meta:
db_table = "website_config" db_table = "website_config"
class JudgeServer(models.Model):
hostname = models.CharField(max_length=64)
ip = models.CharField(max_length=32, blank=True, null=True)
judger_version = models.CharField(max_length=24)
cpu_core = models.IntegerField()
memory_usage = models.FloatField()
cpu_usage = models.FloatField()
last_heartbeat = models.DateTimeField()
create_time = models.DateTimeField(auto_now_add=True)
task_number = models.IntegerField(default=0)
service_url = models.CharField(max_length=128, blank=True, null=True)
class Meta:
db_table = "judge_server"
class JudgeServerToken(models.Model):
token = models.CharField(max_length=32)
class Meta:
db_table = "judge_server_token"

View File

@@ -1,12 +1,12 @@
from utils.api import serializers from utils.api import serializers, DateTimeTZField
from .models import SMTPConfig, WebsiteConfig from .models import SMTPConfig, WebsiteConfig, JudgeServer
class EditSMTPConfigSerializer(serializers.Serializer): class EditSMTPConfigSerializer(serializers.Serializer):
server = serializers.CharField(max_length=128) server = serializers.CharField(max_length=128)
port = serializers.IntegerField(default=25) port = serializers.IntegerField(default=25)
email = serializers.CharField(max_length=128) email = serializers.EmailField(max_length=128)
password = serializers.CharField(max_length=128, required=False, allow_null=True, allow_blank=True) password = serializers.CharField(max_length=128, required=False, allow_null=True, allow_blank=True)
tls = serializers.BooleanField() tls = serializers.BooleanField()
@@ -38,3 +38,22 @@ class WebsiteConfigSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = WebsiteConfig model = WebsiteConfig
exclude = ["id"] exclude = ["id"]
class JudgeServerSerializer(serializers.ModelSerializer):
create_time = DateTimeTZField()
last_heartbeat = DateTimeTZField()
class Meta:
model = JudgeServer
class JudgeServerHeartbeatSerializer(serializers.Serializer):
hostname = serializers.CharField(max_length=64)
judger_version = serializers.CharField(max_length=24)
cpu_core = serializers.IntegerField(min_value=1)
memory = serializers.FloatField(min_value=0, max_value=100)
cpu = serializers.FloatField(min_value=0, max_value=100)
action = serializers.ChoiceField(choices=("heartbeat", ))
service_url = serializers.CharField(max_length=128, required=False)

View File

@@ -1,6 +1,7 @@
from utils.api.tests import APITestCase import hashlib
from .models import SMTPConfig, WebsiteConfig from utils.api.tests import APITestCase
from .models import SMTPConfig, JudgeServerToken, JudgeServer
class SMTPConfigTest(APITestCase): class SMTPConfigTest(APITestCase):
@@ -73,3 +74,51 @@ class WebsiteConfigAPITest(APITestCase):
resp = self.client.get(url) resp = self.client.get(url)
self.assertSuccess(resp) self.assertSuccess(resp)
self.assertEqual(resp.data["data"]["name_shortcut"], "oj") self.assertEqual(resp.data["data"]["name_shortcut"], "oj")
class JudgeServerStatusAPITest(APITestCase):
def setUp(self):
self.url = self.reverse("judge_server_api")
self.user = self.create_super_admin()
def test_get_judge_server_status(self):
self.assertFalse(JudgeServerToken.objects.exists())
resp = self.client.get(self.url)
self.assertSuccess(resp)
self.assertListEqual(resp.data["data"]["servers"], [])
self.assertEqual(JudgeServerToken.objects.first().token, resp.data["data"]["token"])
class JudgeServerHeartbeatest(APITestCase):
def setUp(self):
self.url = self.reverse("judge_server_heartbeat_api")
self.data = {"hostname": "testhostname", "judger_version": "1.0.4", "cpu_core": 4,
"cpu": 90.5, "memory": 80.3, "action": "heartbeat"}
self.token = "test"
self.hashed_token = hashlib.sha256(self.token.encode("utf-8")).hexdigest()
JudgeServerToken.objects.create(token=self.token)
def test_new_heartbeat(self):
resp = self.client.post(self.url, data=self.data, **{"HTTP_X_JUDGE_SERVER_TOKEN": self.hashed_token})
self.assertSuccess(resp)
server = JudgeServer.objects.first()
self.assertEqual(server.ip, "127.0.0.1")
self.assertEqual(server.service_url ,None)
def test_new_heartbeat_service_url(self):
service_url = "http://1.2.3.4:8000/api/judge"
data = self.data
data["service_url"] = service_url
resp = self.client.post(self.url, data=self.data, **{"HTTP_X_JUDGE_SERVER_TOKEN": self.hashed_token})
self.assertSuccess(resp)
server = JudgeServer.objects.first()
self.assertEqual(server.ip, None)
self.assertEqual(server.service_url, service_url)
def test_update_heartbeat(self):
self.test_new_heartbeat()
data = self.data
data["judger_version"] = "2.0.0"
resp = self.client.post(self.url, data=data, **{"HTTP_X_JUDGE_SERVER_TOKEN": self.hashed_token})
self.assertSuccess(resp)
self.assertEqual(JudgeServer.objects.get(hostname=self.data["hostname"]).judger_version, data["judger_version"])

View File

@@ -1,8 +1,9 @@
from django.conf.urls import url from django.conf.urls import url
from ..views import WebsiteConfigAPI, SMTPAPI from ..views import WebsiteConfigAPI, SMTPAPI, JudgeServerAPI
urlpatterns = [ urlpatterns = [
url(r'^smtp$', SMTPAPI.as_view(), name="smtp_admin_api"), url(r'^smtp$', SMTPAPI.as_view(), name="smtp_admin_api"),
url(r'^website$', WebsiteConfigAPI.as_view(), name="website_config_api"), url(r'^website$', WebsiteConfigAPI.as_view(), name="website_config_api"),
url(r'^judge_server', JudgeServerAPI.as_view(), name="judge_server_api")
] ]

View File

@@ -1,7 +1,9 @@
from django.conf.urls import url from django.conf.urls import url
from ..views import WebsiteConfigAPI from ..views import WebsiteConfigAPI, JudgeServerHeartbeatAPI
urlpatterns = [ urlpatterns = [
url(r'^website$', WebsiteConfigAPI.as_view(), name="website_info_api"), url(r'^website$', WebsiteConfigAPI.as_view(), name="website_info_api"),
url(r'^judge_server_heartbeat$', JudgeServerHeartbeatAPI.as_view(), name="judge_server_heartbeat_api")
] ]

View File

@@ -1,11 +1,16 @@
from utils.api import APIView, validate_serializer import hashlib
from django.utils import timezone
from account.decorators import super_admin_required from account.decorators import super_admin_required
from utils.api import APIView, CSRFExemptAPIView, validate_serializer
from utils.shortcuts import rand_str
from .models import SMTPConfig, WebsiteConfig from .models import SMTPConfig, WebsiteConfig, JudgeServer, JudgeServerToken
from .serializers import (WebsiteConfigSerializer, CreateEditWebsiteConfigSerializer, from .serializers import (WebsiteConfigSerializer, CreateEditWebsiteConfigSerializer,
CreateSMTPConfigSerializer, EditSMTPConfigSerializer, CreateSMTPConfigSerializer, EditSMTPConfigSerializer,
SMTPConfigSerializer, TestSMTPConfigSerializer) SMTPConfigSerializer, TestSMTPConfigSerializer,
JudgeServerSerializer, JudgeServerHeartbeatSerializer)
class SMTPAPI(APIView): class SMTPAPI(APIView):
@@ -63,3 +68,65 @@ class WebsiteConfigAPI(APIView):
WebsiteConfig.objects.all().delete() WebsiteConfig.objects.all().delete()
config = WebsiteConfig.objects.create(**data) config = WebsiteConfig.objects.create(**data)
return self.success(WebsiteConfigSerializer(config).data) return self.success(WebsiteConfigSerializer(config).data)
class JudgeServerAPI(APIView):
@super_admin_required
def get(self, request):
judge_server_token = JudgeServerToken.objects.first()
if not judge_server_token:
token = rand_str(12)
JudgeServerToken.objects.create(token=token)
else:
token = judge_server_token.token
servers = JudgeServer.objects.all().order_by("-last_heartbeat")
return self.success({"token": token,
"servers": JudgeServerSerializer(servers, many=True).data})
@super_admin_required
def delete(self, request):
pass
class JudgeServerHeartbeatAPI(CSRFExemptAPIView):
@validate_serializer(JudgeServerHeartbeatSerializer)
def post(self, request):
judge_server_token = JudgeServerToken.objects.first()
if not judge_server_token:
return self.error("Web server token not set")
token = judge_server_token.token
data = request.data
judge_server_token = request.META.get("HTTP_X_JUDGE_SERVER_TOKEN")
if hashlib.sha256(token.encode("utf-8")).hexdigest() != judge_server_token:
return self.error("Invalid token")
service_url = data.get("service_url")
if service_url:
ip = None
else:
ip = request.META["REMOTE_ADDR"]
try:
server = JudgeServer.objects.get(hostname=data["hostname"])
server.judger_version = data["judger_version"]
server.cpu_core = data["cpu_core"]
server.memory_usage = data["memory"]
server.cpu_usage = data["cpu"]
server.service_url= service_url
server.ip = ip
server.last_heartbeat = timezone.now()
server.save()
except JudgeServer.DoesNotExist:
JudgeServer.objects.create(hostname=data["hostname"],
judger_version=data["judger_version"],
cpu_core=data["cpu_core"],
memory_usage=data["memory"],
cpu_usage=data["cpu"],
ip=ip,
service_url=service_url,
last_heartbeat=timezone.now(),
)
return self.success()