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:
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):
server = serializers.CharField(max_length=128)
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)
tls = serializers.BooleanField()
@ -37,4 +37,23 @@ class CreateEditWebsiteConfigSerializer(serializers.Serializer):
class WebsiteConfigSerializer(serializers.ModelSerializer):
class Meta:
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):
@ -73,3 +74,51 @@ class WebsiteConfigAPITest(APITestCase):
resp = self.client.get(url)
self.assertSuccess(resp)
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 ..views import WebsiteConfigAPI, SMTPAPI
from ..views import WebsiteConfigAPI, SMTPAPI, JudgeServerAPI
urlpatterns = [
url(r'^smtp$', SMTPAPI.as_view(), name="smtp_admin_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 ..views import WebsiteConfigAPI
from ..views import WebsiteConfigAPI, JudgeServerHeartbeatAPI
urlpatterns = [
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 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,
CreateSMTPConfigSerializer, EditSMTPConfigSerializer,
SMTPConfigSerializer, TestSMTPConfigSerializer)
SMTPConfigSerializer, TestSMTPConfigSerializer,
JudgeServerSerializer, JudgeServerHeartbeatSerializer)
class SMTPAPI(APIView):
@ -63,3 +68,65 @@ class WebsiteConfigAPI(APIView):
WebsiteConfig.objects.all().delete()
config = WebsiteConfig.objects.create(**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()