Merge branch 'opennebula_api' of github.com:ungleich/dynamicweb into opennebula_api

This commit is contained in:
Levi 2017-05-20 11:20:30 -05:00
commit 03ec8ef674
10 changed files with 223 additions and 68 deletions

View file

@ -231,7 +231,7 @@
<p>2 GiB RAM, </p>
<p>{% trans "15 GiB storage(SSD)" %}</p>
<p>
<a href="#" class="btn btn-primary btn-buynow">{% trans "Buy Now!" %}</a> <a href="#" class="btn btn-default">{% trans "More Info" %}</a> </p>
<a href="{% url 'hosting:login' %}" class="btn btn-primary btn-buynow">{% trans "Buy Now!" %}</a></p>
</div>
</div>
</div>

View file

@ -8,6 +8,8 @@ from django.conf import settings
from hosting.views import RailsHostingView, DjangoHostingView, NodeJSHostingView
from membership import urls as membership_urls
from ungleich_page.views import LandingView
from django.views.generic import RedirectView
from django.core.urlresolvers import reverse_lazy
import debug_toolbar
urlpatterns = [ url(r'^index.html$', LandingView.as_view()),
@ -28,6 +30,7 @@ urlpatterns += i18n_patterns('',
url(r'^/?$', LandingView.as_view()),
url(r'^admin/', include(admin.site.urls)),
url(r'^datacenterlight', include('datacenterlight.urls', namespace="datacenterlight")),
url(r'^hosting/', RedirectView.as_view(url=reverse_lazy('hosting:login')), name='redirect_hosting_login'),
url(r'^alplora', include('alplora.urls', namespace="alplora")),
url(r'^membership/', include(membership_urls)),
url(r'^digitalglarus/', include('digitalglarus.urls',

View file

@ -41,7 +41,7 @@ class HostingPlan(models.Model):
for cfg in cls.objects.all()]
def price(self):
price = self.disk_size * 0.2
price = self.disk_size * 0.6
price += self.cpu_cores * 5
price += self.memory * 2
return price

View file

@ -5,9 +5,19 @@
<div class="container dashboard-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="col-md-12">
<br/>
{% if messages %}
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
{% endif %}
</div>
{% if not error %}
<h3><i class="fa fa-server" aria-hidden="true"></i> {% trans "New Virtual Machine"%} </h3>
<hr/>
<form method="POST" action="">
{% csrf_token %}
<div class="form-group">
@ -34,6 +44,7 @@
<button class="btn btn-success" >{% trans "Start VM"%} </button>
</div>
</form>
{% endif %}
</div>

View file

@ -87,7 +87,7 @@
</div><!--/row-->
<div class="row">
<div class="col-md-12">
{% trans "Configuration"%}: {{virtual_machine.get_configuration_display}}
{% trans "Configuration"%}: {{virtual_machine.configuration}}
</div>
</div>

View file

@ -7,10 +7,6 @@
<div class="col-md-8 col-md-offset-2" style="margin-top: 35px;">
<table class="table borderless table-hover">
<h3 class="pull-left"><i class="fa fa-server" aria-hidden="true"></i> {% trans "Virtual Machines"%} </h3>
<p class="pull-right">
<a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>
</p>
<br/>
<div class="col-md-12">
<br/>
{% if messages %}
@ -21,6 +17,12 @@
</div>
{% endif %}
</div>
{% if not error %}
<p class="pull-right">
<a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>
</p>
<br/>
<thead>
<tr>
<th>{% trans "ID"%}</th>
@ -53,6 +55,7 @@
{% endfor %}
</tbody>
</table>
{% endif %}
{% if is_paginated %}
<div class="pagination">

View file

@ -37,6 +37,8 @@ from opennebula_api.serializers import VirtualMachineSerializer,\
from oca.exceptions import OpenNebulaException
from oca.pool import WrongNameError
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a backend \
connection error. please try again in a few minutes."
class DjangoHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/django.html"
@ -528,8 +530,13 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data
try:
vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data
except ConnectionRefusedError:
messages.error( request,
'In order to create a VM, you need to create/upload your SSH KEY first.'
)
return context
@ -564,9 +571,28 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
queryset = manager.get_vms()
serializer = VirtualMachineSerializer(queryset, many=True)
return serializer.data
try:
queryset = manager.get_vms()
serializer = VirtualMachineSerializer(queryset, many=True)
return serializer.data
except ConnectionRefusedError:
messages.error( self.request,
'We could not load your VMs due to a backend connection \
error. Please try again in a few minutes'
)
self.kwargs['error'] = 'connection'
return []
def get_context_data(self, **kwargs):
error = self.kwargs.get('error')
if error is not None:
print(error)
context = { 'error' : 'connection' }
else:
context = super(ListView, self).get_context_data(**kwargs)
return context
class CreateVirtualMachinesView(LoginRequiredMixin, View):
@ -586,14 +612,24 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
)
return HttpResponseRedirect(reverse('hosting:key_pair'))
manager = OpenNebulaManager()
templates = manager.get_templates()
configuration_options = HostingPlan.get_serialized_configs()
try:
manager = OpenNebulaManager()
templates = manager.get_templates()
configuration_options = HostingPlan.get_serialized_configs()
context = {
'templates': VirtualMachineTemplateSerializer(templates, many=True).data,
'configuration_options' : configuration_options,
}
except:
messages.error( request,
'We could not load the VM templates due to a backend connection \
error. Please try again in a few minutes'
)
context = {
'error' : 'connection'
}
context = {
'templates': VirtualMachineTemplateSerializer(templates, many=True).data,
'configuration_options' : configuration_options,
}
return render(request, self.template_name, context)
def post(self, request):
@ -622,10 +658,16 @@ class VirtualMachineView(LoginRequiredMixin, View):
vm_id = self.kwargs.get('pk')
try:
vm = manager.get_vm(vm_id)
return vm
except ConnectionRefusedError:
messages.error( self.request,
'We could not load your VM due to a backend connection \
error. Please try again in a few minutes'
)
return None
except Exception as error:
print(error)
raise Http404()
return vm
def get_success_url(self):
final_url = reverse('hosting:virtual_machines')
@ -633,10 +675,14 @@ class VirtualMachineView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
vm = self.get_object()
serializer = VirtualMachineSerializer(vm)
context = {
'virtual_machine': serializer.data,
}
try:
serializer = VirtualMachineSerializer(vm)
context = {
'virtual_machine': serializer.data,
}
except:
pass
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):

View file

@ -84,8 +84,12 @@ class OpenNebulaManager():
vm_pool.info()
except AttributeError:
logger.info('Could not connect via client, using oneadmin instead')
vm_pool = oca.VirtualMachinePool(self.oneadmin_client)
vm_pool.info(filter=-2)
try:
vm_pool = oca.VirtualMachinePool(self.oneadmin_client)
vm_pool.info(filter=-2)
return vm_pool
except:
raise ConnectionRefusedError
except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
@ -93,13 +97,16 @@ class OpenNebulaManager():
protocol=settings.OPENNEBULA_PROTOCOL)
)
raise ConnectionRefusedError
return vm_pool
# For now we'll just handle all other errors as connection errors
except:
raise ConnectionRefusedError
def get_vms(self):
try:
return self._get_vm_pool()
except ConnectionRefusedError:
return []
raise ConnectionRefusedError
def get_vm(self, vm_id):
vm_id = int(vm_id)
@ -107,7 +114,7 @@ class OpenNebulaManager():
vm_pool = self._get_vm_pool()
return vm_pool.get_by_id(vm_id)
except:
return None
raise ConnectionRefusedError
def create_template(self, name, cores, memory, disk_size, core_price, memory_price,
disk_size_price, ssh='' ):
@ -146,26 +153,56 @@ class OpenNebulaManager():
<MEMORY>{memory}</MEMORY>
<VCPU>{vcpu}</VCPU>
<CPU>{cpu}</CPU>
<DISK>
<CONTEXT>
<SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY>
</CONTEXT>
"""
try:
disk = template.template.disks[0]
image_id = disk.image_id
vm_specs = vm_specs_formatter.format(
vcpu=int(specs['cpu']),
cpu=0.1* int(specs['cpu']),
memory=1024 * int(specs['memory']),
ssh=ssh_key
)
vm_specs += """<DISK>
<TYPE>fs</TYPE>
<SIZE>{size}</SIZE>
<DEV_PREFIX>vd</DEV_PREFIX>
</DISK>
<CONTEXT>
<SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY>
</CONTEXT>
</TEMPLATE>
"""
vm_id = template.instantiate(name ='',
pending=False,
extra_template=vm_specs_formatter.format(
<IMAGE_ID>{image_id}</IMAGE_ID>
</DISK>
</TEMPLATE>
""".format(size=1024 * int(specs['disk_size']),
image_id=image_id)
except:
disk = template.template.disks[0]
image = disk.image
image_uname = disk.image_uname
vm_specs = vm_specs_formatter.format(
vcpu=int(specs['cpu']),
cpu=0.1* int(specs['cpu']),
size=1024 * int(specs['disk_size']),
memory=1024 * int(specs['memory']),
ssh=ssh_key
)
)
)
vm_specs += """<DISK>
<TYPE>fs</TYPE>
<SIZE>{size}</SIZE>
<DEV_PREFIX>vd</DEV_PREFIX>
<IMAGE>{image}</IMAGE>
<IMAGE_UNAME>{image_uname}</IMAGE_UNAME>
</DISK>
</TEMPLATE>
""".format(size=1024 * int(specs['disk_size']),
image=image,
image_uname=image_uname)
vm_id = template.instantiate(name ='',
pending=False,
extra_template=vm_specs, )
try:
self.oneadmin_client.call(
@ -203,13 +240,16 @@ class OpenNebulaManager():
try:
template_pool = oca.VmTemplatePool(self.oneadmin_client)
template_pool.info()
return template_pool
except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
host=settings.OPENNEBULA_DOMAIN,
protocol=settings.OPENNEBULA_PROTOCOL)
)
raise ConnectionRefusedError
return template_pool
except:
raise ConnectionRefusedError
def get_templates(self):
try:
@ -220,6 +260,14 @@ class OpenNebulaManager():
]
return public_templates
except ConnectionRefusedError:
raise ConnectionRefusedError
except:
raise ConnectionRefusedError
def try_get_templates(self):
try:
return self.get_templates()
except:
return []
def get_template(self, template_id):
@ -228,7 +276,7 @@ class OpenNebulaManager():
template_pool = self._get_template_pool()
return template_pool.get_by_id(template_id)
except:
return None
raise ConnectionRefusedError

View file

@ -17,9 +17,6 @@ class VirtualMachineTemplateSerializer(serializers.Serializer):
disk_size = serializers.SerializerMethodField()
set_memory = serializers.IntegerField(write_only=True, label='Memory')
memory = serializers.SerializerMethodField()
core_price = serializers.FloatField(source='template.cpu_cost')
disk_size_price = serializers.FloatField(source='template.disk_cost')
memory_price = serializers.FloatField(source='template.memory_cost')
price = serializers.SerializerMethodField()
def create(self, validated_data):
@ -30,9 +27,6 @@ class VirtualMachineTemplateSerializer(serializers.Serializer):
name = data.pop('name')
disk_size = data.pop('disk')
memory = template.pop('memory')
core_price = template.pop('cpu_cost')
memory_price = template.pop('memory_cost')
disk_size_price = template.pop('disk_cost')
manager = OpenNebulaManager()
try:
@ -50,16 +44,23 @@ class VirtualMachineTemplateSerializer(serializers.Serializer):
def get_disk_size(self, obj):
template = obj.template
disk_size = 0
for disk in template.disks:
disk_size += int(disk.size)
return disk_size / 1024
try:
for disk in template.disks:
disk_size += int(disk.size)
return disk_size / 1024
except:
return 0
def get_price(self, obj):
template = obj.template
price = float(template.cpu) * float(template.cpu_cost)
price += (int(template.memory)/1024 * float(template.memory_cost))
for disk in template.disks:
price += int(disk.size)/1024 * float(template.disk_cost)
price = float(template.cpu) * 5.0
price += (int(template.memory)/1024 * 2.0)
try:
for disk in template.disks:
price += int(disk.size)/1024 * 0.6
except:
pass
return price
def get_memory(self, obj):
@ -87,12 +88,15 @@ class VirtualMachineSerializer(serializers.Serializer):
state = serializers.CharField(read_only=True, source='str_state')
price = serializers.SerializerMethodField()
ssh_key = serializers.CharField(write_only=True)
configuration = serializers.SerializerMethodField()
template_id = serializers.ChoiceField(
choices=[(key.id, key.name) for key in
OpenNebulaManager().get_templates()],
OpenNebulaManager().try_get_templates()
],
source='template.template_id',
write_only=True
write_only=True,
default=[]
)
def create(self, validated_data):
@ -134,8 +138,12 @@ class VirtualMachineSerializer(serializers.Serializer):
def get_price(self, obj):
template = obj.template
price = float(template.cpu) * float(template.cpu_cost)
price += (int(template.memory)/1024 * float(template.memory_cost))
price = float(template.vcpu) * 5.0
price += (int(template.memory)/1024 * 2.0)
for disk in template.disks:
price += int(disk.size)/1024 * float(template.disk_cost)
price += int(disk.size)/1024 * 0.6
return price
def get_configuration(self, obj):
template_id = obj.template.template_id
template = OpenNebulaManager().get_template(template_id)
return template.name

View file

@ -12,6 +12,12 @@ from guardian.mixins import PermissionRequiredMixin
from .serializers import VirtualMachineTemplateSerializer, \
VirtualMachineSerializer
from .models import OpenNebulaManager
from rest_framework.exceptions import APIException
class ServiceUnavailable(APIException):
status_code = 503
default_detail = 'Service temporarily unavailable, try again later.'
default_code = 'service_unavailable'
class TemplateCreateView(generics.ListCreateAPIView):
@ -37,7 +43,14 @@ class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView):
def get_queryset(self):
manager = OpenNebulaManager()
return manager.get_templates()
# We may have ConnectionRefusedError if we don't have a
# connection to OpenNebula. For now, we raise ServiceUnavailable
try:
templates = manager.get_templates()
except ConnectionRefusedError:
raise ServiceUnavailable
return templates
class VmCreateView(generics.ListCreateAPIView):
"""This class handles the GET and POST requests."""
@ -48,7 +61,13 @@ class VmCreateView(generics.ListCreateAPIView):
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
return manager.get_vms()
# We may have ConnectionRefusedError if we don't have a
# connection to OpenNebula. For now, we raise ServiceUnavailable
try:
vms = manager.get_vms()
except ConnectionRefusedError:
raise ServiceUnavailable
return vms
def perform_create(self, serializer):
"""Save the post data when creating a new template."""
@ -64,17 +83,34 @@ class VmDetailsView(generics.RetrieveUpdateDestroyAPIView):
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
return manager.get_vms()
# We may have ConnectionRefusedError if we don't have a
# connection to OpenNebula. For now, we raise ServiceUnavailable
try:
vms = manager.get_vms()
except ConnectionRefusedError:
raise ServiceUnavailable
return vms
def get_object(self):
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
return manager.get_vm(self.kwargs.get('pk'))
# We may have ConnectionRefusedError if we don't have a
# connection to OpenNebula. For now, we raise ServiceUnavailable
try:
vm = manager.get_vm(self.kwargs.get('pk'))
except ConnectionRefusedError:
raise ServiceUnavailable
return vm
def perform_destroy(self, instance):
owner = self.request.user
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
manager.delete_vm(instance.id)
# We may have ConnectionRefusedError if we don't have a
# connection to OpenNebula. For now, we raise ServiceUnavailable
try:
manager.delete_vm(instance.id)
except ConnectionRefusedError:
raise ServiceUnavailable