Partial fixes for issues pointed by @rscnt

- Moved opennebula constants to base.py
- Refactored all opennebula functions from admin.py to opennebula_functions.py
- string#format for some strings
- Renamed my_view function to show_vms
- Added logger to show logs of exceptions generated
This commit is contained in:
M.Ravi 2017-04-24 18:51:11 +02:00
parent af3818d64a
commit a8952700d6
4 changed files with 261 additions and 203 deletions

View file

@ -474,3 +474,33 @@ else:
ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch' ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch'
GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance' GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance'
#############################################
# configurations for opennebula-integration #
#############################################
# The oneadmin user name of the OpenNebula infrastructure
OPENNEBULA_USERNAME = 'oneadmin'
# The oneadmin password of the OpenNebula infrastructure
# The default credentials of the Sandbox OpenNebula VM is
# oneadmin:opennebula
OPENNEBULA_PASSWORD = 'opennebula'
# The protocol is generally http or https
OPENNEBULA_PROTOCOL = 'http'
# The ip address or the domain name of the opennebula infrastructure
# OPENNEBULA_DOMAIN = '192.168.182.173'
# OPENNEBULA_DOMAIN = '192.168.122.225'
# OPENNEBULA_DOMAIN = '192.168.182.176'
OPENNEBULA_DOMAIN = '192.168.42.35'
# The port to connect in order to send an xmlrpc request. The default
# port is 2633
OPENNEBULA_PORT = '2633'
# The endpoint to which the XML RPC request needs to be sent to. The
# default value is /RPC2
OPENNEBULA_ENDPOINT = '/RPC2'

View file

@ -19,33 +19,3 @@ INSTALLED_APPS+=(
'django_extensions', 'django_extensions',
'debug_toolbar' 'debug_toolbar'
) )
#############################################
# configurations for opennebula-integration #
#############################################
# The user name of the OpenNebula infrastructure
OPENNEBULA_USERNAME = 'oneadmin'
# The password of the OpenNebula infrastructure
# The default credentials of the Sandbox OpenNebula VM is
# oneadmin:opennebula
OPENNEBULA_PASSWORD = 'opennebula'
# The protocol is generally http or https
OPENNEBULA_PROTOCOL = 'http'
# The ip address or the domain name of the opennebula infrastructure
# OPENNEBULA_DOMAIN = '192.168.182.173'
# OPENNEBULA_DOMAIN = '192.168.122.225'
#OPENNEBULA_DOMAIN = '192.168.182.176'
OPENNEBULA_DOMAIN = '192.168.42.35'
# The port to connect in order to send an xmlrpc request. The default
# port is 2633
OPENNEBULA_PORT = '2633'
# The endpoint to which the XML RPC request needs to be sent to. The
# default value is /RPC2
OPENNEBULA_ENDPOINT = '/RPC2'

View file

@ -1,23 +1,13 @@
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html from django.utils.html import format_html
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf.urls import url
from django.template.response import TemplateResponse
from django.conf import settings
from utils.mailer import BaseEmail
from django import template
from django.shortcuts import redirect
from django.contrib import messages
from django.contrib.auth.signals import user_logged_in
import random
import string
import oca from utils.mailer import BaseEmail
import socket from django.contrib.auth.signals import user_logged_in
from oca.exceptions import OpenNebulaException
from .forms import HostingOrderAdminForm from .forms import HostingOrderAdminForm
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, ManageVM from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, ManageVM
from .opennebula_functions import HostingManageVMAdmin, user_logged_in_callback
class HostingOrderAdmin(admin.ModelAdmin): class HostingOrderAdmin(admin.ModelAdmin):
# fields = ('slug', 'imdb_link', 'start', 'finish', 'added_by') # fields = ('slug', 'imdb_link', 'start', 'finish', 'added_by')
@ -103,166 +93,10 @@ class VirtualMachinePlanAdmin(admin.ModelAdmin):
email.send() email.send()
obj.save() obj.save()
class HostingManageVMAdmin(admin.ModelAdmin):
client = None
def get_urls(self):
urls = super().get_urls()
my_urls = [
url(r'^$', self.admin_site.admin_view(self.my_view, cacheable=True), name='showvms'),
url(r'^create_vm/$', self.admin_site.admin_view(self.create_vm, cacheable=True), name='createvm'),
url(r'^delete_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.delete_vm, cacheable=True), name='deletevm'),
url(r'^stop_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.stop_vm, cacheable=True), name='stopvm'),
url(r'^start_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.start_vm, cacheable=True), name='startvm'),
]
return my_urls + urls
# Function to initialize opennebula client based on the logged in
# user
def init_opennebula_client(self, opennebula_user):
opennebula_user_password = get_random_password()
self.client = oca.Client(opennebula_user + ':' + opennebula_user_password, settings.OPENNEBULA_PROTOCOL + '://' + settings.OPENNEBULA_DOMAIN + ':' + settings.OPENNEBULA_PORT + settings.OPENNEBULA_ENDPOINT)
# Function that lists the VMs of the current user
def my_view(self, request):
try :
self.init_opennebula_client(str(request.user))
vm_pool = oca.VirtualMachinePool(self.client)
vm_pool.info()
except socket.timeout:
messages.add_message(request, messages.ERROR, "Socket timeout error.")
except OpenNebulaException:
messages.add_message(request, messages.ERROR, "OpenNebulaException occurred.")
except OSError as err:
messages.add_message(request, messages.ERROR, str("OS error: {0}".format(err)))
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
vms = vm_pool,
)
return TemplateResponse(request, "hosting/managevms.html", context)
# Creating VM by using method allocate(client, template)
def create_vm(self, request):
message = ''
# check if the request contains the template parameter, if it is
# not set warn the user of setting this.
vm_template = request.POST.get('vm_template')
if vm_template == 'select' :
messages.add_message(request, messages.ERROR, "Please select a vm template")
else :
try :
# We do have the vm_template param set. Get and parse it
# and check it to be in the desired range.
vm_template_int = int(vm_template)
if vm_template_int >=1 and vm_template_int <= 8:
# Lets create a test VM with 128MB of ram and 1 CPU
vm_id = oca.VirtualMachine.allocate(self.client, '<VM><MEMORY>' + str(1024 * vm_template_int) + '</MEMORY><VCPU>' + str(vm_template_int)+ '</VCPU><CPU>' + str(0.1 * vm_template_int) + '</CPU><DISK><TYPE>fs</TYPE><SIZE>' + str(10000 * vm_template_int) + '</SIZE></DISK></VM>')
message = "Created with id = " + str(vm_id)
vm_pool = self.get_vms()
messages.add_message(request, messages.SUCCESS, message)
else:
messages.add_message(request, messages.ERROR, "Please select an appropriate value for vm template.")
except socket.timeout:
messages.add_message(request, messages.ERROR, "Socket timeout error.")
except OpenNebulaException:
messages.add_message(request, messages.ERROR, "OpenNebulaException occurred.")
except OSError as err:
messages.add_message(request, messages.ERROR, str("OS error: {0}".format(err)))
except ValueError:
messages.add_message(request, messages.ERROR, "Please select an appropriate value for vm template.")
return redirect('admin:showvms')
# Retrives virtual machine pool information
def get_vms(self):
vm_pool = oca.VirtualMachinePool(self.client)
vm_pool.info()
return vm_pool
# Delete VM from the pool and DB by using method finalize()
def delete_vm(self, request, vmid):
# get the desired vm from the pool
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else :
print("Deleting vm_id = " + str(vm_id) + " state = " + vm.str_state)
if vm.str_state == 'PENDING' or vm.str_state =='POWEROFF' or vm.str_state =='ACTIVE':
vm.delete()
messages.add_message(request, messages.SUCCESS, "Deleted from " + vm.str_state + " state vm with id = " + str(vm_id))
else:
vm.finalize()
messages.add_message(request, messages.SUCCESS, "Deleted (using finalize()) from " + vm.str_state + " state vm with id = " + str(vm_id))
return redirect('admin:showvms')
def stop_vm(self, request, vmid):
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else :
vm.stop()
messages.add_message(request, messages.SUCCESS, "Stopped the vm with id = " + str(vm_id))
return redirect('admin:showvms')
def start_vm(self, request, vmid):
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else :
vm.resume()
messages.add_message(request, messages.SUCCESS, "Started the vm with id = " + str(vm_id))
return redirect('admin:showvms')
def get_vm_by_id(self, vmid):
vms = self.get_vms()
vms
for vm in vms:
if vm.id == vmid :
return vm
return -1
# callback for creating opennebula users on user login
def user_logged_in_callback(sender, request, user, **kwargs):
client = oca.Client(settings.OPENNEBULA_USERNAME + ':' + settings.OPENNEBULA_PASSWORD, settings.OPENNEBULA_PROTOCOL + '://' + settings.OPENNEBULA_DOMAIN + ':' + settings.OPENNEBULA_PORT + settings.OPENNEBULA_ENDPOINT)
# Notes:
# 1. python-oca library's oca.User.allocate(client, user, pass)
# method does not work with python-oca version oca-4.15.0a1-py3.5
# This is because the call is missing a fourth parameter
# auth_driver.
# To overcome this issue, we make a direct call to xml-rpc method
# 'user.allocate' passing this fourth parameter.
#
# 2. We have a dummy authentication driver in opennebula and we
# use this so as to avoid opennebula authentication. However, we
# need to supply a dummy password. Without this, we can not
# create an OpenNebula user. We use dummy string 'a' as password
# for all users.
#
# 3. We user the user's email as the user name.
if find_opennebula_user_by_name(str(user.email), client) == -1 :
user_id = client.call('user.allocate', str(user.email), get_random_password(), 'dummy')
print("User " + str(user.email) + " does not exist. Created the user. User id = " + str(user_id))
# Finds if an OpenNebula user with user_name exists. Returns the
# OpenNebula user if it exists, -1 otherwise.
def find_opennebula_user_by_name(user_name, client):
pool = oca.UserPool(client)
pool.info()
for user in pool:
if user.name == user_name :
return user
return -1
# Returns random password that is needed by OpenNebula
def get_random_password():
return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(20))
user_logged_in.connect(user_logged_in_callback)
admin.site.register(HostingOrder, HostingOrderAdmin) admin.site.register(HostingOrder, HostingOrderAdmin)
admin.site.register(VirtualMachineType) admin.site.register(VirtualMachineType)
admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin) admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
user_logged_in.connect(user_logged_in_callback)
admin.site.register(ManageVM, HostingManageVMAdmin) admin.site.register(ManageVM, HostingManageVMAdmin)

View file

@ -0,0 +1,224 @@
import logging
import socket
import string
import oca
import random
from django.conf import settings
from django.conf.urls import url
from django.contrib import admin
from django.contrib import messages
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from oca.exceptions import OpenNebulaException
# Get an instance of a logger
logger = logging.getLogger(__name__)
class HostingManageVMAdmin(admin.ModelAdmin):
client = None
def get_urls(self):
urls = super().get_urls()
my_urls = [
url(r'^$', self.admin_site.admin_view(self.show_vms, cacheable=True), name='showvms'),
url(r'^create_vm/$', self.admin_site.admin_view(self.create_vm, cacheable=True), name='createvm'),
url(r'^delete_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.delete_vm, cacheable=True),
name='deletevm'),
url(r'^stop_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.stop_vm, cacheable=True), name='stopvm'),
url(r'^start_vm/(?P<vmid>\d+)/$', self.admin_site.admin_view(self.start_vm, cacheable=True),
name='startvm'),
]
return my_urls + urls
# Function to initialize opennebula client based on the logged in
# user
def init_opennebula_client(self, opennebula_user):
if self.client is None:
opennebula_user_password = get_random_password()
self.client = oca.Client("{0}:{1}".format(opennebula_user, opennebula_user_password),
"{protocol}://{domain}:{port}{endpoint}".format(
protocol=settings.OPENNEBULA_PROTOCOL,
domain=settings.OPENNEBULA_DOMAIN,
port=settings.OPENNEBULA_PORT,
endpoint=settings.OPENNEBULA_ENDPOINT
))
# Function that shows the VMs of the current user
def show_vms(self, request):
vm_pool = None
try:
self.init_opennebula_client(request.user)
vm_pool = oca.VirtualMachinePool(self.client)
vm_pool.info()
except socket.timeout:
messages.add_message(request, messages.ERROR, "Socket timeout error.")
except OpenNebulaException:
messages.add_message(request, messages.ERROR, "OpenNebulaException occurred.")
except OSError as err:
messages.add_message(request, messages.ERROR, str("OS error: {0}".format(err)))
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
vms=vm_pool,
)
return TemplateResponse(request, "hosting/managevms.html", context)
# Creating VM by using method allocate(client, template)
def create_vm(self, request):
message = ''
# check if the request contains the template parameter, if it is
# not set warn the user of setting this.
vm_template = request.POST.get('vm_template')
if vm_template == 'select':
messages.add_message(request, messages.ERROR, "Please select a vm template")
else:
try:
# We do have the vm_template param set. Get and parse it
# and check it to be in the desired range.
# We have 8 possible VM templates for the moment which are 1x, 2x, 4x ...
# the basic template of 10GB disk, 1GB ram, 1 vcpu, 0.1 cpu
vm_template_int = int(vm_template)
if 1 <= vm_template_int <= 8:
vm_string_formatter = """
<VM>
<MEMORY>
{memory}
</MEMORY>
<VCPU>
{vcpu}
</VCPU>
<CPU>
{cpu}
</CPU>
<DISK>
<TYPE>
{disk_type}
</TYPE>
<SIZE>
{size}
</SIZE>
</DISK>
</VM>
"""
vm_id = oca.VirtualMachine.allocate(self.client,
vm_string_formatter.format(
memory=1024 * vm_template_int,
vcpu=vm_template_int,
cpu=0.1 * vm_template_int,
disk_type='fs',
size=10000 * vm_template_int))
message = "Created with id = " + str(vm_id)
messages.add_message(request, messages.SUCCESS, message)
else:
messages.add_message(request, messages.ERROR,
"Please select an appropriate value for vm template.")
except socket.timeout as socket_err:
messages.add_message(request, messages.ERROR, "Socket timeout error.")
logger.error("Socket timeout error: {0}".format(socket_err))
except OpenNebulaException as opennebula_err:
messages.add_message(request, messages.ERROR, "OpenNebulaException occurred.")
logger.error("OpenNebulaException error: {0}".format(opennebula_err))
except OSError as os_err:
messages.add_message(request, messages.ERROR, str("OS error: {0}".format(os_err)))
logger.error("OSError : {0}".format(os_err))
except ValueError as value_err:
messages.add_message(request, messages.ERROR,
"Please select an appropriate value for vm template.")
logger.error("ValueError : {0}".format(value_err))
return redirect('admin:showvms')
# Retrives virtual machine pool information
def get_vms(self):
vm_pool = oca.VirtualMachinePool(self.client)
vm_pool.info()
return vm_pool
# Delete VM from the pool and DB by using method finalize()
def delete_vm(self, request, vmid):
# get the desired vm from the pool
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else:
print("Deleting vm_id = " + str(vm_id) + " state = " + vm.str_state)
if vm.str_state == 'PENDING' or vm.str_state == 'POWEROFF' or vm.str_state == 'ACTIVE':
vm.delete()
messages.add_message(request, messages.SUCCESS,
"Deleted from " + vm.str_state + " state vm with id = " + str(vm_id))
else:
vm.finalize()
messages.add_message(request, messages.SUCCESS,
"Deleted (using finalize()) from " + vm.str_state + " state vm with id = " + str(
vm_id))
return redirect('admin:showvms')
def stop_vm(self, request, vmid):
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else:
vm.stop()
messages.add_message(request, messages.SUCCESS, "Stopped the vm with id = " + str(vm_id))
return redirect('admin:showvms')
def start_vm(self, request, vmid):
vm_id = int(vmid)
vm = self.get_vm_by_id(vm_id)
if vm == -1:
messages.add_message(request, messages.ERROR, "Did not find a vm with id = " + str(vm_id))
else:
vm.resume()
messages.add_message(request, messages.SUCCESS, "Started the vm with id = " + str(vm_id))
return redirect('admin:showvms')
def get_vm_by_id(self, vmid):
vms = self.get_vms()
for vm in vms:
if vm.id == vmid:
return vm
return -1
# callback for creating opennebula users on user login
def user_logged_in_callback(sender, request, user, **kwargs):
client = oca.Client(settings.OPENNEBULA_USERNAME + ':' + settings.OPENNEBULA_PASSWORD,
settings.OPENNEBULA_PROTOCOL + '://' + settings.OPENNEBULA_DOMAIN + ':' + settings.OPENNEBULA_PORT + settings.OPENNEBULA_ENDPOINT)
# Notes:
# 1. python-oca library's oca.User.allocate(client, user, pass)
# method does not work with python-oca version oca-4.15.0a1-py3.5
# This is because the call is missing a fourth parameter
# auth_driver.
# To overcome this issue, we make a direct call to xml-rpc method
# 'user.allocate' passing this fourth parameter.
#
# 2. We have a dummy authentication driver in opennebula and we
# use this so as to avoid opennebula authentication. However, we
# need to supply a dummy password. Without this, we can not
# create an OpenNebula user. We use dummy string 'a' as password
# for all users.
#
# 3. We user the user's email as the user name.
if find_opennebula_user_by_name(str(user.email), client) == -1:
user_id = client.call('user.allocate', str(user.email), get_random_password(), 'dummy')
print("User " + str(user.email) + " does not exist. Created the user. User id = " + str(user_id))
# Finds if an OpenNebula user with user_name exists. Returns the
# OpenNebula user if it exists, -1 otherwise.
def find_opennebula_user_by_name(user_name, client):
pool = oca.UserPool(client)
pool.info()
for user in pool:
if user.name == user_name:
return user
return -1
# Returns random password that is needed by OpenNebula
def get_random_password():
return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(20))