diff --git a/README.md b/README.md index 8ec15f5..cd5c4e5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ * User can add SSH public key to root in Instance (Tested only Ubuntu) * User can change root password in Instance (Tested only Ubuntu) +* Supports cloud-init datasource interface ### Warning!!! @@ -201,7 +202,7 @@ webvirtcloud RUNNING pid 24185, uptime 2:59:14 #### Apache mod_wsgi configuration ``` WSGIDaemonProcess webvirtcloud threads=2 maximum-requests=1000 display-name=webvirtcloud -WSGIScriptAlias / /srv/webvirtcloud/webvirtcloud/wsgi.py +WSGIScriptAlias / /srv/webvirtcloud/webvirtcloud/wsgi_custom.py ``` #### Install final required packages for libvirtd and others on Host Server @@ -219,6 +220,14 @@ login: admin password: admin +### Cloud-init +Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows. +``` +datasource: + OpenStack: + metadata_urls: [ "http://webvirtcloud.domain.com/datasource" ] +``` + ### How To Update ```bash git pull diff --git a/accounts/templates/accounts-list.html b/accounts/templates/accounts-list.html new file mode 100644 index 0000000..c6d5796 --- /dev/null +++ b/accounts/templates/accounts-list.html @@ -0,0 +1,175 @@ +{% extends "base.html" %} +{% load i18n %} +{% load staticfiles %} +{% block title %}{% trans "Users" %}{% endblock %} +{% block content %} + +
+
+ {% include 'create_user_block.html' %} + +

{% trans "Users" %}

+
+
+ + + {% include 'errors_block.html' %} + +
+ {% if not users %} +
+
+ + {% trans "Warning:" %} {% trans "You don't have any User" %} +
+
+ {% else %} +
+ + + + + + + + + + + + {% for user in users %} + + + + + + + + {% endfor %} + +
UsernameStatusStaffSuperuserClone
+ {{ user.username }} + + + + + {% if user.is_active %} + {% trans "Active" %} + {% else %} + {% trans "Blocked" %} + {% endif %} + {% if user.is_staff %}{% endif %}{% if user.is_superuser %}{% endif %}{% if user.userattributes.can_clone_instances %}{% endif %}
+ + {% for user in users %} + + + {% endfor %} +
+ {% endif %} +
+{% endblock %} +{% block script %} + +{% endblock %} diff --git a/accounts/templates/create_user_block.html b/accounts/templates/create_user_block.html index f4b4679..55e5c2f 100644 --- a/accounts/templates/create_user_block.html +++ b/accounts/templates/create_user_block.html @@ -1,6 +1,6 @@ {% load i18n %} {% if request.user.is_superuser %} - + @@ -8,12 +8,12 @@ diff --git a/accounts/views.py b/accounts/views.py index 5fdfb4d..00bd392 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -130,7 +130,10 @@ def accounts(request): user_delete.delete() return HttpResponseRedirect(request.get_full_path()) - return render(request, 'accounts.html', locals()) + accounts_template_file = 'accounts.html' + if settings.VIEW_ACCOUNTS_STYLE == "list": + accounts_template_file = 'accounts-list.html' + return render(request, accounts_template_file, locals()) @login_required diff --git a/datasource/__init__.py b/datasource/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/datasource/admin.py b/datasource/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/datasource/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/datasource/migrations/__init__.py b/datasource/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/datasource/models.py b/datasource/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/datasource/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/datasource/templates/user_data b/datasource/templates/user_data new file mode 100644 index 0000000..4a94483 --- /dev/null +++ b/datasource/templates/user_data @@ -0,0 +1,6 @@ +#cloud-config +{% if instance_keys %} +ssh_authorized_keys: +{% for key in instance_keys %} - {{ key }}{% endfor %} +{% endif %} + diff --git a/datasource/tests.py b/datasource/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/datasource/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/datasource/urls.py b/datasource/urls.py new file mode 100644 index 0000000..9183d41 --- /dev/null +++ b/datasource/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url +from . import views + +urlpatterns = [ + url(r'^openstack/$', + views.os_index, name='ds_openstack_index'), + url(r'^openstack/(?P[\w\-\.]+)/meta_data.json$', + views.os_metadata_json, name='ds_openstack_metadata'), + url(r'^openstack/(?P[\w\-\.]+)/user_data$', + views.os_userdata, name='ds_openstack_userdata'), +] diff --git a/datasource/views.py b/datasource/views.py new file mode 100644 index 0000000..9264717 --- /dev/null +++ b/datasource/views.py @@ -0,0 +1,64 @@ +from django.shortcuts import render +from django.http import HttpResponse, Http404 +from accounts.models import UserInstance, UserSSHKey +import json +import socket + +OS_VERSIONS = [ 'latest', '' ] +OS_UUID = "iid-dswebvirtcloud" + +def os_index(request): + response = '\n'.join(OS_VERSIONS) + return HttpResponse(response) + +def os_metadata_json(request, version): + """ + :param request: + :param version: + :return: + """ + + if version == 'latest': + ip = get_client_ip(request) + hostname = get_hostname_by_ip(ip) + response = { 'uuid': OS_UUID, 'hostname': hostname } + return HttpResponse(json.dumps(response)) + else: + err = 'Invalid version: %s' % version + raise Http404(err) + +def os_userdata(request, version): + """ + :param request: + :param version: + :return: + """ + if version == 'latest': + ip = get_client_ip(request) + hostname = get_hostname_by_ip(ip) + vname = hostname.split('.')[0] + + instance_keys = [] + userinstances = UserInstance.objects.filter(instance__name=vname) + + for ui in userinstances: + keys = UserSSHKey.objects.filter(user=ui.user) + for k in keys: + instance_keys.append(k.keypublic) + + return render(request, 'user_data', locals()) + else: + err = 'Invalid version: %s' % version + raise Http404(err) + +def get_client_ip(request): + x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') + if x_forwarded_for: + ip = x_forwarded_for.split(',')[-1].strip() + else: + ip = request.META.get('REMOTE_ADDR') + return ip + +def get_hostname_by_ip(ip): + addrs = socket.gethostbyaddr(ip) + return addrs[0] diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 2b73c0a..f6dc355 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -712,6 +712,27 @@ +

{% trans "To set console listen address, shutdown the instance." %}

+
{% csrf_token %} +
+ +
+ +
+
+ {% ifequal status 5 %} + + {% else %} + + {% endifequal %} +
+
+

{% trans "To create console password, shutdown the instance." %}

{% csrf_token %}
@@ -1273,6 +1294,13 @@ $("#console_select_type option[value='" + console_type + "']").prop('selected', true); } }); + $(document).ready(function () { + // set current console listen address or fall back to default + var console_listen_address = "{{ console_listen_address }}" + if (console_listen_address != '') { + $("#console_select_listen_address option[value='" + console_listen_address + "']").prop('selected', true); + } + }); {% if not request.user.is_superuser %} $('#select_clone_name').on('change', function () { update_clone_disk_name($(this).val()); diff --git a/instances/views.py b/instances/views.py index a00e5b1..5be3adb 100644 --- a/instances/views.py +++ b/instances/views.py @@ -22,7 +22,6 @@ from vrtManager.connection import connection_manager from vrtManager.create import wvmCreate from vrtManager.util import randomPasswd from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE -from webvirtcloud.settings import QEMU_KEYMAPS, QEMU_CONSOLE_TYPES from logs.views import addlogmsg from django.conf import settings @@ -186,8 +185,9 @@ def instance(request, compute_id, vname): computes_count = computes.count() users = User.objects.all().order_by('username') publickeys = UserSSHKey.objects.filter(user_id=request.user.id) - keymaps = QEMU_KEYMAPS - console_types = QEMU_CONSOLE_TYPES + keymaps = settings.QEMU_KEYMAPS + console_types = settings.QEMU_CONSOLE_TYPES + console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES try: userinstace = UserInstance.objects.get(instance__compute_id=compute_id, instance__name=vname, @@ -617,6 +617,13 @@ def instance(request, compute_id, vname): msg = _("Set VNC type") addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#vncsettings') + + if 'set_console_listen_address' in request.POST: + console_type = request.POST.get('console_listen_address', '') + conn.set_console_listen_addr(console_type) + msg = _("Set VNC listen address") + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#vncsettings') if request.user.is_superuser: if 'migrate' in request.POST: diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 14feab1..72144e6 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -455,6 +455,32 @@ class wvmInstance(wvmConnect): return "127.0.0.1" return listen_addr + def set_console_listen_addr(self, listen_addr): + xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE) + root = ElementTree.fromstring(xml) + console_type = self.get_console_type() + try: + graphic = root.find("devices/graphics[@type='%s']" % console_type) + except SyntaxError: + # Little fix for old version ElementTree + graphic = root.find("devices/graphics") + if graphic is None: + return False + listen = graphic.find("listen[@type='address']") + if listen is None: + return False + if listen_addr: + graphic.set("listen", listen_addr) + listen.set("address", listen_addr) + else: + try: + graphic.attrib.pop("listen") + listen.attrib.pop("address") + except: + pass + newxml = ElementTree.tostring(root) + return self._defineXML(newxml) + def get_console_socket(self): socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket") diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 9e5c821..b7ba1ea 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -29,6 +29,7 @@ INSTALLED_APPS = ( 'logs', 'accounts', 'create', + 'datasource', ) MIDDLEWARE_CLASSES = ( @@ -44,6 +45,7 @@ MIDDLEWARE_CLASSES = ( AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', + #'django.contrib.auth.backends.RemoteUserBackend', #'accounts.backends.MyRemoteUserBackend', ) @@ -104,6 +106,12 @@ QEMU_CONSOLE_TYPES = ['vnc', 'spice'] # default console type QEMU_CONSOLE_DEFAULT_TYPE = 'vnc' +# list of console listen addresses +QEMU_CONSOLE_LISTEN_ADDRESSES = ( + ('127.0.0.1', 'Localhost'), + ('0.0.0.0', 'All interfaces'), +) + # list taken from http://qemu.weilnetz.de/qemu-doc.html#sec_005finvocation QEMU_KEYMAPS = ['ar', 'da', 'de', 'de-ch', 'en-gb', 'en-us', 'es', 'et', 'fi', 'fo', 'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it', @@ -123,6 +131,10 @@ ALLOW_EMPTY_PASSWORD = True SHOW_ACCESS_ROOT_PASSWORD = False SHOW_ACCESS_SSH_KEYS = False SHOW_PROFILE_EDIT_PASSWORD = False + +# available: default (grid), list +VIEW_ACCOUNTS_STYLE = 'grid' + INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2' INSTANCE_VOLUME_DEFAULT_BUS = 'virtio' INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync' diff --git a/webvirtcloud/urls.py b/webvirtcloud/urls.py index 8cfa9d9..e5ab3fc 100644 --- a/webvirtcloud/urls.py +++ b/webvirtcloud/urls.py @@ -9,6 +9,7 @@ urlpatterns = patterns('', url(r'^accounts/', include('accounts.urls')), url(r'^computes/', include('computes.urls')), url(r'^logs/', include('logs.urls')), + url(r'^datasource/', include('datasource.urls')), url(r'^compute/(?P[0-9]+)/storages/$', 'storages.views.storages', name='storages'),