Commit 31560893 by Bodor Máté

Merge branch 'DEV' into 'CI'

# Conflicts:
#   Pipfile
#   Pipfile.lock
#   recircle/recircle/settings.py
#   recircle/recircle/urls.py
parents d816a212 353a668c
Pipeline #741 failed with stages
in 28 seconds
.vscode/ .vscode/
recircle/db.sqlite3 recircle/db.sqlite3
interface-openstack/ interface-openstack/
recircle/clouds.yaml
recircle/implementation/
recircle/interface/
recircle/interface_openstack/
...@@ -6,12 +6,16 @@ verify_ssl = true ...@@ -6,12 +6,16 @@ verify_ssl = true
[dev-packages] [dev-packages]
httpie = "*" httpie = "*"
flake8 = "*" flake8 = "*"
django-rest-swagger = "*"
[packages] [packages]
django = "*" django = "*"
django-rest-framework = "*"
djangorestframework = "*" djangorestframework = "*"
djoser = "*" djoser = "*"
django-cors-headers = "*"
openstacksdk = "*"
python-novaclient = "*"
keystoneauth1 = "*"
[requires] [requires]
python_version = "3.6" python_version = "3.6"
# from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class ImageConfig(AppConfig):
name = "image"
# Generated by Django 2.2.1 on 2019-05-13 12:02
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Disk',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, help_text='Name of the disk', max_length=100, verbose_name='name')),
('remote_ID', models.CharField(help_text='ID, which helps access the disk', max_length=40, unique=True, verbose_name='remote_ID')),
],
),
]
from django.db import models
class Disk(models.Model):
"""A virtual disk.
"""
name = models.CharField(
blank=True, max_length=100, verbose_name="name", help_text="Name of the disk"
)
remote_ID = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the disk",
)
from rest_framework import serializers
from .models import Disk
class DiskSerializer(serializers.ModelSerializer):
class Meta:
model = Disk
fields = ("name", "remote_ID")
# from django.test import TestCase
# Create your tests here.
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from image import views
urlpatterns = [path("", views.DiskList.as_view())]
urlpatterns = format_suffix_patterns(urlpatterns)
from image.models import Disk
from image.serializers import DiskSerializer
# from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from interface_openstack.implementation.image.openstack_image_manager import (
OpenstackImageManager,
)
import openstack
conn = openstack.connect(cloud="openstack")
class DiskList(APIView):
def get(self, request, format=None):
# OpenStack
interface = OpenstackImageManager(conn)
return Response([disk.__dict__ for disk in interface.list()])
# Create response
disks = Disk.object.all()
serializer = DiskSerializer(disks, many=True)
return Response(serializer.data)
from django.contrib import admin
from instance.models import Instance
admin.site.register(Instance)
from django.apps import AppConfig
class InstanceConfig(AppConfig):
name = "instance"
# Generated by Django 2.2 on 2019-04-17 14:01
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Instance',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, help_text='Human readable name of instance.', max_length=100, verbose_name='name')),
('description', models.TextField(blank=True, help_text='The description of the instance.', verbose_name='description')),
],
),
]
# Generated by Django 2.2 on 2019-05-02 13:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='instance',
name='access_method',
field=models.CharField(choices=[('rdp', 'Remote Desktop Protocol'), ('ssh', 'Secure Shell')], default='rdp', help_text='Primary remote access method.', max_length=10),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='lease',
field=models.CharField(choices=[('project', 'Project'), ('server', 'Server'), ('infinite', 'Infinite lease')], default='project', help_text='Expiration method', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='password',
field=models.CharField(default=12345678, help_text='Original password of the instance.', max_length=50),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='system',
field=models.CharField(default='windows', help_text='Operating system type', max_length=50),
preserve_default=False,
),
migrations.AlterField(
model_name='instance',
name='description',
field=models.TextField(blank=True, help_text='The description of the instance'),
),
migrations.AlterField(
model_name='instance',
name='name',
field=models.CharField(help_text='Human readable name of instance', max_length=100),
),
]
# Generated by Django 2.2.1 on 2019-05-10 13:07
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('instance', '0002_auto_20190502_1341'),
]
operations = [
migrations.AddField(
model_name='instance',
name='deleted',
field=models.BooleanField(default=False, help_text='Indicates if the instance is ready for garbage collection'),
),
migrations.AddField(
model_name='instance',
name='time_of_delete',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='After this point in time, the instance will be deleted!'),
preserve_default=False,
),
migrations.AddField(
model_name='instance',
name='time_of_suspend',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='After this point in time, the instance will be suspended'),
preserve_default=False,
),
migrations.AlterField(
model_name='instance',
name='access_method',
field=models.CharField(choices=[('rdp', 'Remote Desktop Protocol'), ('ssh', 'Secure Shell')], help_text='Primary remote access method', max_length=10),
),
migrations.AlterField(
model_name='instance',
name='lease',
field=models.CharField(choices=[('shortlab', 'Short laboratory'), ('lab', 'Laboratory'), ('project', 'Project')], help_text='Expiration method', max_length=50),
),
migrations.AlterField(
model_name='instance',
name='password',
field=models.CharField(help_text='Original password of the instance', max_length=50),
),
]
# Generated by Django 2.2.1 on 2019-05-13 12:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0003_auto_20190510_1307'),
]
operations = [
migrations.AddField(
model_name='instance',
name='remoteId',
field=models.CharField(default=0, help_text='ID of the instance on the backend', max_length=100),
preserve_default=False,
),
]
# Generated by Django 2.2.1 on 2019-05-13 12:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instance', '0004_instance_remoteid'),
]
operations = [
migrations.RenameField(
model_name='instance',
old_name='remoteId',
new_name='remote_id',
),
]
from django.db import models
from django.conf import settings
ACCESS_METHODS = tuple(
[(key, val[0]) for key, val in settings.VM_ACCESS_PROTOCOLS.items()]
)
# Later stored in database
LEASE_TYPES = tuple(
[(val["name"], val["verbose_name"]) for val in settings.LEASE_TYPES]
)
class Instance(models.Model):
name = models.CharField(max_length=100, help_text="Human readable name of instance")
remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend"
)
description = models.TextField(
blank=True, help_text="The description of the instance"
)
access_method = models.CharField(
max_length=10, choices=ACCESS_METHODS, help_text="Primary remote access method"
)
system = models.CharField(max_length=50, help_text="Operating system type")
password = models.CharField(
max_length=50, help_text="Original password of the instance"
)
lease = models.CharField(
max_length=50, choices=LEASE_TYPES, help_text="Expiration method"
)
time_of_suspend = models.DateTimeField(
help_text="After this point in time, the instance will be suspended"
)
time_of_delete = models.DateTimeField(
help_text="After this point in time, the instance will be deleted!"
)
deleted = models.BooleanField(
help_text="Indicates if the instance is ready for garbage collection",
default=False,
)
# template
# disks
# owner
from rest_framework import serializers
from .models import Instance
class InstanceSerializer(serializers.ModelSerializer):
class Meta:
model = Instance
fields = "__all__"
# from django.test import TestCase
# Create your tests here.
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from instance import views
urlpatterns = [
path("instances/", views.InstanceList.as_view()),
path("instances/<int:pk>/", views.InstanceDetail.as_view()),
# path('instances/<int:pk>/action/', views.InstanceAction.as_view())
]
urlpatterns = format_suffix_patterns(urlpatterns)
from instance.models import Instance
from instance.serializers import InstanceSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from interface_openstack.implementation.vm.instance import OSVirtualMachineManager
import openstack
import datetime
conn = openstack.connect(cloud="openstack")
class InstanceList(APIView):
def get(self, request, format=None):
instances = Instance.objects.all()
return Response(InstanceSerializer(instances, many=True).data)
def post(self, request, format=None):
data = request.data
interface = OSVirtualMachineManager(conn)
imageId = "da51253f-867c-472d-8ce0-81e7b7126d60"
flavorId = "1"
networks = [{"uuid": "c03d0d4b-413e-4cc6-9ebe-c0b5ca0dac3a"}]
newBackendInstance = interface.create_vm_from_template(
"Integration test vm 2", imageId, flavorId, networks
)
newInstance = Instance(
name=data["name"],
remote_id=newBackendInstance.id,
description=data["description"],
access_method="ssh",
system=data["system"],
password="12345678",
lease=data["lease"],
time_of_suspend=datetime.datetime.now(),
time_of_delete=datetime.datetime.now(),
deleted=False,
)
newInstance.save()
return Response(newInstance.pk)
class InstanceDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Instance.objects.get(pk=pk)
except Instance.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
instance = self.get_object(pk)
instanceDict = InstanceSerializer(instance).data
interface = OSVirtualMachineManager(conn)
remoteInstance = interface.get_vm(instance.remote_id)
remoteInstanceDict = remoteInstance.__dict__
merged_dict = {**instanceDict, **remoteInstanceDict}
return Response(merged_dict)
def put(self, request, pk, format=None):
instance = self.get_object(pk)
serializer = InstanceSerializer(instance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
instance = self.get_object(pk)
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
import os import os
import sys import sys
if __name__ == '__main__': if __name__ == "__main__":
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recircle.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recircle.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
......
...@@ -20,68 +20,69 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ...@@ -20,68 +20,69 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'xhe8=w50lz82gjb6+z%(rwk2c+1kd!%(iv_s^!tp)*5cnb=-^t' SECRET_KEY = "xhe8=w50lz82gjb6+z%(rwk2c+1kd!%(iv_s^!tp)*5cnb=-^t"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = [ ALLOWED_HOSTS = ["vm.niif.cloud.bme.hu", "localhost"]
'vm.niif.cloud.bme.hu',
'localhost',
]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', "instance.apps.InstanceConfig",
'django.contrib.auth', "image.apps.ImageConfig",
'django.contrib.contenttypes', "django.contrib.admin",
'django.contrib.sessions', "django.contrib.auth",
'django.contrib.messages', "django.contrib.contenttypes",
'django.contrib.staticfiles', "django.contrib.sessions",
'rest_framework', "django.contrib.messages",
'djoser', "django.contrib.staticfiles",
"rest_framework",
"djoser",
"rest_framework_swagger",
"corsheaders",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "corsheaders.middleware.CorsMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.common.CommonMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
ROOT_URLCONF = 'recircle.urls' ROOT_URLCONF = "recircle.urls"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [], "DIRS": [],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ]
},
}, },
}
] ]
WSGI_APPLICATION = 'recircle.wsgi.application' WSGI_APPLICATION = "recircle.wsgi.application"
# Database # Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.sqlite3', "ENGINE": "django.db.backends.sqlite3",
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
} }
} }
...@@ -91,26 +92,20 @@ DATABASES = { ...@@ -91,26 +92,20 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
}, },
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/ # https://docs.djangoproject.com/en/2.1/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = "en-us"
TIME_ZONE = 'UTC' TIME_ZONE = "UTC"
USE_I18N = True USE_I18N = True
...@@ -122,4 +117,43 @@ USE_TZ = True ...@@ -122,4 +117,43 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/ # https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_URL = "/static/"
###############################################################################
# RECIRCLE CONFIG
###############################################################################
# VM ACCESS PROTOCOLS
VM_ACCESS_PROTOCOLS = {
"rdp": ["Remote Desktop Protocol", 3389, "tcp"],
"ssh": ["Secure Shell", 22, "tcp"],
}
# LEASE TYPES
LEASE_TYPES = [
{
"name": "shortlab",
"verbose_name": "Short laboratory",
"description": "The student only needs the VM during the lecture.",
"suspend_interval_seconds": 10800, # 3 hours
"delete_interval_seconds": 14400, # 4 hours
},
{
"name": "lab",
"verbose_name": "Laboratory",
"description": "The student may need the VM after the lecture.",
"suspend_interval_seconds": 18000, # 5 hours
"delete_interval_seconds": 604800, # 1 week
},
{
"name": "project",
"verbose_name": "Project",
"description": "A VM is used for a project for a semester.",
"suspend_interval_seconds": 2629743, # 1 month
"delete_interval_seconds": 15778463, # 6 months
},
]
# CORS config for development version only
CORS_ORIGIN_WHITELIST = ("http://localhost:3000",)
...@@ -15,9 +15,16 @@ Including another URLconf ...@@ -15,9 +15,16 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, re_path, include from django.urls import path, re_path, include
from rest_framework_swagger.views import get_swagger_view
schema_view = get_swagger_view(title="RECIRCLE API")
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path("images/", include("image.urls")),
re_path(r'^auth/', include('djoser.urls')), path("api/v1/", include("instance.urls")),
re_path(r'^auth/', include('djoser.urls.authtoken')) path("admin/", admin.site.urls),
re_path(r"^auth/", include("djoser.urls")),
re_path(r"^auth/", include("djoser.urls.authtoken")),
path(r"swagger", schema_view),
] ]
...@@ -11,6 +11,6 @@ import os ...@@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recircle.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recircle.settings")
application = get_wsgi_application() application = get_wsgi_application()
# from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class StorageConfig(AppConfig):
name = "storage"
from django.db import models
# Create your models here.
class DataStore:
"""Collection of virtual disks.
"""
name = models.CharField(
max_length=100,
unique=True,
verbose_name="name",
help_text="Name of the data store.",
)
remote_ID = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the data store.",
)
# vm
from rest_framework import serializers
from .models import DataStore
class DataStoreSerializer(serializers.ModelSerializer):
class Meta:
model = DataStore
fields = ("name", "remote_ID")
# from django.test import TestCase
# Create your tests here.
# from django.shortcuts import render
# Create your views here.
# from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class TemplateConfig(AppConfig):
name = "template"
from django.db import models
class InstanceTemplate:
"""Virtual machine template.
"""
name = models.CharField(
max_length=100,
verbose_name="name",
help_text="Human readable name of template.",
)
description = models.TextField(
verbose_name="description", blank=True, help_text="Description of the template."
)
# owner = models.ForeignKey(User)
remote_ID = models.CharField(
max_length=40,
unique=True,
verbose_name="remote_ID",
help_text="ID, which helps access the template.",
)
from rest_framework import serializers
from .models import InstanceTemplate
class InstanceTemplateSerializer(serializers.ModelSerializer):
class Meta:
model = InstanceTemplate
fields = ("name", "description", "owner", "remote_ID")
# from django.test import TestCase
# Create your tests here.
# from django.shortcuts import render
# Create your views here.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment