From d642f7909de5af80dd951c7a2b2a728acc309b37 Mon Sep 17 00:00:00 2001 From: Loic Baron Date: Wed, 17 Dec 2014 17:36:39 +0100 Subject: [PATCH] FORGE: Pierre's work for Students Labs --- forge/__init__.py | 0 forge/forms.py | 17 ++++ forge/models.py | 118 +++++++++++++++++++++++ forge/script | 1 + forge/tasks.py | 145 +++++++++++++++++++++++++++++ forge/templates/create-course.html | 10 ++ forge/templates/createCourse.html | 14 +++ forge/templates/createLab.html | 29 ++++++ forge/templates/labDetails.html | 14 +++ forge/templates/list.html | 31 ++++++ forge/templates/listCourse.html | 73 +++++++++++++++ forge/templates/mainView.html | 14 +++ forge/tests.py | 16 ++++ forge/views.py | 132 ++++++++++++++++++++++++++ 14 files changed, 614 insertions(+) create mode 100644 forge/__init__.py create mode 100644 forge/forms.py create mode 100644 forge/models.py create mode 160000 forge/script create mode 100644 forge/tasks.py create mode 100644 forge/templates/create-course.html create mode 100644 forge/templates/createCourse.html create mode 100644 forge/templates/createLab.html create mode 100644 forge/templates/labDetails.html create mode 100644 forge/templates/list.html create mode 100644 forge/templates/listCourse.html create mode 100644 forge/templates/mainView.html create mode 100644 forge/tests.py create mode 100644 forge/views.py diff --git a/forge/__init__.py b/forge/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/forge/forms.py b/forge/forms.py new file mode 100644 index 00000000..402b3c33 --- /dev/null +++ b/forge/forms.py @@ -0,0 +1,17 @@ +# -*- coding:utf-8 -*- + +from django import forms +from models import Lab, Course +from django.db import models + +class LabForm(forms.ModelForm): + title = forms.CharField(label= "Title") + subject = forms.FileField(label="Subject of the lab") + class Meta: + model = Lab + +class courseForm(forms.Form): + lab = forms.ModelChoiceField(queryset= Lab.objects.all(), empty_label="Select a Lab") + nbEnv = forms.IntegerField(min_value=0, label="Number of environment") + subnet = forms.CharField(initial='10.1.0.0/16') + diff --git a/forge/models.py b/forge/models.py new file mode 100644 index 00000000..e0b3ace2 --- /dev/null +++ b/forge/models.py @@ -0,0 +1,118 @@ +# -*- coding:utf-8 -*- + +from django.db import models + +# Used for automatically delete file +from django.dispatch import receiver +import os + +# Class to describe a service set on a host +class Service(models.Model): + serviceName = models.CharField(max_length=50) + servicePort = models.CharField(max_length=10) + host = models.ForeignKey('Host') + + def __unicode__(self): + if self.servicePort != u'': + return u"%s on %s" % (self.serviceName, self.servicePort) + else: + return u"%s" % self.serviceName + +# Class to describe an interface available on a host +class Interface(models.Model): + ip = models.CharField(max_length=16) + name = models.CharField(max_length=40) + host = models.ForeignKey('Host') + + def __unicode__(self): + return u"%s -> %s" % (self.name, self.ip) + +# Class to describe a host +class Host(models.Model): + TYPE_CHOICES = ( + ('PUB', 'Public'), + ('PRIV', 'Private'), + ('COM', 'Common'), + ) + hostname = models.CharField(max_length=100) + hostType = models.CharField(max_length=20, choices=TYPE_CHOICES, default='PRIV') + pleSlice = models.ForeignKey('Slice') + latitude = models.FloatField() + longitude = models.FloatField() + + def __unicode__(self): + return u"%s %s" % (self.hostname, self.hostType) + +# Class to describe a slice (sliceName and reference to environment) +class Slice(models.Model): + sliceName = models.CharField(max_length=50) + environment = models.ForeignKey('Environment') + + def __unicode__(self): + return u"%s" % self.sliceName + +# Class to describe a student environment (sshKey and reference to a course) +class Environment(models.Model): + sshKey = models.FileField(upload_to='ict_education/keys') + course = models.ForeignKey('Course') + confFile = models.FileField(upload_to='ict_education/xmlFiles') + linkFile = models.FileField(upload_to='ict_education/xmlFiles') + ready = models.BooleanField(default=False) + +# function used to automatically delete the stored file when deleting the model from the database +@receiver(models.signals.post_delete, sender = Environment) +def environment_delete_ssh_key(sender, instance, **kwargs): + if instance.sshKey: + if os.path.isfile(instance.sshKey.path): + os.remove(instance.sshKey.path) + if instance.confFile: + if os.path.isfile(instance.confFile.path): + os.remove(instance.confFile.path) + if instance.linkFile: + if os.path.isfile(instance.linkFile.path): + os.remove(instance.linkFile.path) + +# Class to describe a course (ie a set of environment) (reference to lab, mainKey used by the teacher to access all node) +class Course(models.Model): + lab = models.ForeignKey('Lab') + mainKey = models.FileField(upload_to='ict_education/keys', null = True) + ready = models.BooleanField(default=False) + sliceName = models.CharField(max_length=50) + + def __unicode__(self): + return u"%s %s" % (self.lab, self.sliceName) + +# function used to automatically delete the stored file when deleting the model from the database +@receiver(models.signals.post_delete, sender = Course) +def course_delete_main_ssh_key(sender, instance, **kwargs): + if instance.mainKey: + if os.path.isfile(instance.mainKey.path): + os.remove(instance.mainKey.path) + +# Class to describe a lab +class Lab(models.Model): + title = models.CharField(max_length=100) + author = models.CharField(max_length=40) + subject = models.FileField(upload_to='ict_education/Labs/subjects') + configurationFile = models.FileField(upload_to='ict_education/Labs/xmlFiles') + linkFile = models.FileField(upload_to='ict_education/Labs/xmlFiles') + + def __unicode__(self): + return u"%s %s" % (self.title, self.author) + +# function used to automatically delete the stored file when deleting the model from the database +@receiver(models.signals.post_delete, sender = Lab) +def lab_delete_files(sender, instance, **kwargs): + # Remove the subject + if instance.subject: + if os.path.isfile(instance.subject.path): + os.remove(instance.subject.path) + # Remove the configuration file + if instance.configurationFile: + if os.path.isfile(instance.configurationFile.path): + os.remove(instance.configurationFile.path) + # Remove the link file + if instance.linkFile: + if os.path.isfile(instance.linkFile.path): + os.remove(instance.linkFile.path) + diff --git a/forge/script b/forge/script new file mode 160000 index 00000000..c0c898f8 --- /dev/null +++ b/forge/script @@ -0,0 +1 @@ +Subproject commit c0c898f8ded230e9a144df9e02d547004613fa8e diff --git a/forge/tasks.py b/forge/tasks.py new file mode 100644 index 00000000..cf3b9965 --- /dev/null +++ b/forge/tasks.py @@ -0,0 +1,145 @@ +# -*- coding:utf-8 -*- +from django.core.files import File +from celery import task, current_task +from djcelery.models import TaskState +from xmlrpclib import Fault + +from forge.models import * + +import forge.script.Auth as Auth +import time + +# Import class used to create the environment +from forge.script.request import TransformRawXml +from forge.script.openvswitch import TransformXml + +# Import the settings +from django.conf import settings + +@task() +def taskInstallEnvironment(env): + env.ready = False + env.save() + #TODO Change directory location + curDir = os.getcwd() + os.chdir('./ict_education/script/') + try: + xml = TransformXml(confFile = curDir+env.confFile.url, linkFile = curDir+env.linkFile.url, keyFile = curDir+env.course.mainKey.url) + xml.clearConf() + xml.setSliceConf() + xml.setLinks() + xml.setRoutes() + xml.setServices() + except Exception, why: + print why + os.chdir(curDir) + env.ready = True + env.save() + +@task() +def taskDeleteCourse(course): + environments = course.environment_set.all() + #TODO Change directory location + curDir = os.getcwd() + os.chdir('./ict_education/script/') + for env in environments: + try: + xml = TransformXml(confFile = curDir+env.confFile.url, linkFile = curDir+env.linkFile.url, keyFile = curDir+env.course.mainKey.url) + xml.clearConf() + except Exception, why: + print why + try: + rawXml = TransformRawXml(sliceName=course.sliceName) + rawXml.deleteSlice() + except Fault, why: + print "I got an xmlrpc Fault" + print why + os.chdir(curDir) + course.delete() + +@task() +def taskRenewSlice(course): + rawXml = TransformRawXml(sliceName=course.sliceName) + rawXml.renewSlice() + +@task() +def taskCreateCourse(form): + newCourse = Course() + newCourse.lab = Lab.objects.get(id=form.cleaned_data['lab'].id) + newCourse.ready = False + newCourse.sliceName = form.cleaned_data['sliceName'] + newCourse.save() + #TODO Change directory location + curDir = os.getcwd() + os.chdir('./ict_education/script/') + if form.cleaned_data['keyLocation'] == '': + keyLocation = None + else: + keyLocation = form.cleaned_data['keyLocation'] + if form.cleaned_data['url'] == '': + sliceUrl = 'http://onelab.eu' + else: + sliceUrl = form.cleaned_data['url'] + if form.cleaned_data['description'] == '': + sliceDescription = 'Slice used for educational purpose' + else: + sliceDescription = form.cleaned_data['url'] + try: + rawXml = TransformRawXml(confFile = settings.MEDIA_ROOT+newCourse.lab.configurationFile.name, linkFile = settings.MEDIA_ROOT+newCourse.lab.linkFile.name, subnet = form.cleaned_data['subnet'], sliceName = form.cleaned_data['sliceName'], nbEnv = form.cleaned_data['nbEnv'], sliceUrl = sliceUrl, sliceDescription = sliceDescription, mainKeyPriv = keyLocation) + rawXml.setSlice() + newCourse.mainKey.save(rawXml.mainKeyPriv.split('/')[-1], File(open(rawXml.mainKeyPriv))) + newCourse.save() + i = 0 + for environment in rawXml.envList: + newEnv = Environment() + newEnv.ready = False + newEnv.course = newCourse + newEnv.sshKey.save(rawXml.envKeyPriv[i].split('/')[-1], File(open(rawXml.envKeyPriv[i]))) + newEnv.confFile.save(rawXml.envConfFile[i].split('/')[-1], File(open(rawXml.envConfFile[i]))) + newEnv.linkFile.save(rawXml.envLinkFile[i].split('/')[-1], File(open(rawXml.envLinkFile[i]))) + newEnv.save() + i += 1 + for plSlice in environment: + newSlice = Slice() + newSlice.sliceName = plSlice.slice_name + newSlice.environment = newEnv + newSlice.save() + for host in plSlice.hosts: + newHost = Host() + nodeSelected = '' + for env in rawXml.nodeList: + for node in env: + if node['hostname'] == host.url: + nodeselected = node + break + if nodeSelected != '': + break + newHost.hostname = host.url + newHost.pleSlice = newSlice + newHost.latitude = nodeSelected['latitude'] + newHost.longitude = nodeSelected['longitude'] + newHost.save() + for serviceName, port in host.services.services: + newService = Service() + newService.serviceName = serviceName + newService.servicePort = str(port) + newService.host = newHost + newService.save() + for device in host.devices: + newInterface = Interface() + newInterface.ip = device.ip + newInterface.name = device.id_dev + newInterface.host = newHost + newInterface.save() + newEnv.ready = True + newEnv.save() + os.chdir(curDir) + newCourse.ready = True + newCourse.save() + except Exception, why: + print "An error occured deleting the environment" + print why + rawXml = TransformRawXml(sliceName=newCourse.sliceName) + rawXml.deleteSlice() + raise Exception('Global Fault') + return 0 diff --git a/forge/templates/create-course.html b/forge/templates/create-course.html new file mode 100644 index 00000000..5b06bfbb --- /dev/null +++ b/forge/templates/create-course.html @@ -0,0 +1,10 @@ +
+
+
+
+ {{ form.as_p }} +

+
+
+
+
diff --git a/forge/templates/createCourse.html b/forge/templates/createCourse.html new file mode 100644 index 00000000..26b5b1df --- /dev/null +++ b/forge/templates/createCourse.html @@ -0,0 +1,14 @@ + + + + + Create a new Class + + +
+ {% csrf_token %} + {{ form.as_p }} +

+
+ + diff --git a/forge/templates/createLab.html b/forge/templates/createLab.html new file mode 100644 index 00000000..2a0b67d4 --- /dev/null +++ b/forge/templates/createLab.html @@ -0,0 +1,29 @@ + + + + + Create a new Lab + + + + {% if labs %} + + {% else %} +

No lab

+ {% endif %} + + +
+ {% csrf_token %} + {{ form.as_p }} +

+
+ + diff --git a/forge/templates/labDetails.html b/forge/templates/labDetails.html new file mode 100644 index 00000000..0ab10cc0 --- /dev/null +++ b/forge/templates/labDetails.html @@ -0,0 +1,14 @@ + + + + + Details of lab {{ lab.title }} + + +

{{ lab.title }}

+

{{ lab.author }}

+

Subject

+

Configuration File

+

{{ lab.linkFile.name }}

+ + diff --git a/forge/templates/list.html b/forge/templates/list.html new file mode 100644 index 00000000..42c2579b --- /dev/null +++ b/forge/templates/list.html @@ -0,0 +1,31 @@ + + + + + Minimal Django File Upload Example + + + + {% if documents %} + + {% else %} +

No documents.

+ {% endif %} + + +
+ {% csrf_token %} +

{{ form.non_field_errors }}

+

{{ form.docfile.label_tag }} {{ form.docfile.help_text }}

+

+ {{ form.docfile.errors }} + {{ form.docfile }} +

+

+
+ + diff --git a/forge/templates/listCourse.html b/forge/templates/listCourse.html new file mode 100644 index 00000000..0c510595 --- /dev/null +++ b/forge/templates/listCourse.html @@ -0,0 +1,73 @@ + + + + + List of all your class + + + {% if courses %} + + {% else %} +

You have not created a class yet

+ {% endif %} + + diff --git a/forge/templates/mainView.html b/forge/templates/mainView.html new file mode 100644 index 00000000..6f5ae310 --- /dev/null +++ b/forge/templates/mainView.html @@ -0,0 +1,14 @@ + + + + + ICT Education + + + +

Create a new lab

+

Create a new course

+

List of your course

+ + +