diff --git a/computes/templates/overview.html b/computes/templates/overview.html index 34e68c5..466c10e 100644 --- a/computes/templates/overview.html +++ b/computes/templates/overview.html @@ -11,6 +11,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • @@ -20,6 +23,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/computes/urls.py b/computes/urls.py index 58a0497..4b4ab6d 100644 --- a/computes/urls.py +++ b/computes/urls.py @@ -1,9 +1,22 @@ from django.conf.urls import url -from . import views +from storages.views import storages, storage +from networks.views import networks, network +from secrets.views import secrets +from create.views import create_instance +from interfaces.views import interfaces, interface +from computes.views import overview, compute_graph +from instances.views import instances urlpatterns = [ - url(r'^$', views.computes, name='computes'), - url(r'^overview/(?P[0-9]+)/$', views.overview, name='overview'), - url(r'^statistics/(?P[0-9]+)/$', - views.compute_graph, name='compute_graph'), + url(r'^(?P[0-9]+)/$', overview, name='overview'), + url(r'^(?P[0-9]+)/statistics$', compute_graph, name='compute_graph'), + url(r'^(?P[0-9]+)/instances/$', instances, name='compute_instances'), + url(r'^(?P[0-9]+)/storages/$', storages, name='storages'), + url(r'^(?P[0-9]+)/storage/(?P[\w\-\.\/]+)/$', storage, name='storage'), + url(r'^(?P[0-9]+)/networks/$', networks, name='networks'), + url(r'^(?P[0-9]+)/network/(?P[\w\-\.]+)/$', network, name='network'), + url(r'^(?P[0-9]+)/interfaces/$', interfaces, name='interfaces'), + url(r'^(?P[0-9]+)/interface/(?P[\w\-\.\:]+)/$', interface, name='interface'), + url(r'^(?P[0-9]+)/secrets/$', secrets, name='secrets'), + url(r'^(?P[0-9]+)/create/$', create_instance, name='create_instance'), ] diff --git a/computes/views.py b/computes/views.py index 18ad680..93e3f5e 100644 --- a/computes/views.py +++ b/computes/views.py @@ -134,6 +134,28 @@ def computes(request): error_messages.append(msg_err.as_text()) return render(request, 'computes.html', locals()) +@login_required +def compute_instances(request, compute_id): + """ + :param request: + :return: + """ + if not request.user.is_superuser: + return HttpResponseRedirect(reverse('index')) + error_messages = [] + compute = get_object_or_404(Compute, pk=compute_id) + try: + conn = wvmHostDetails(compute.hostname, + compute.login, + compute.password, + compute.type) + hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info() + hypervisor = conn.hypervisor_type() + mem_usage = conn.get_memory_usage() + conn.close() + except libvirtError as lib_err: + error_messages.append(lib_err) + return render(request, 'compute_instances.html', locals()) @login_required def overview(request, compute_id): diff --git a/console/templates/console-vnc-full.html b/console/templates/console-vnc-full.html index 6149289..ff6ee98 100755 --- a/console/templates/console-vnc-full.html +++ b/console/templates/console-vnc-full.html @@ -233,7 +233,7 @@
  • - +
  • @@ -332,4 +332,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/console/templates/console-vnc-lite.html b/console/templates/console-vnc-lite.html index 4dd7bfe..0502a62 100755 --- a/console/templates/console-vnc-lite.html +++ b/console/templates/console-vnc-lite.html @@ -220,6 +220,7 @@ //var port = WebUtil.getConfigVar('port', window.location.port); var host = '{{ ws_host }}'; var port = '{{ ws_port }}'; + var wspath = '{{ ws_path }}'; // if port == 80 (or 443) then it won't be present and should be // set manually @@ -263,7 +264,7 @@ url = 'ws'; } - url += '://' + host; + url += '://' + host + wspath; if(port) { url += ':' + port; } diff --git a/console/views.py b/console/views.py index 4196ff0..70d0186 100644 --- a/console/views.py +++ b/console/views.py @@ -7,6 +7,8 @@ from instances.models import Instance from vrtManager.instance import wvmInstance from webvirtcloud.settings import WS_PORT from webvirtcloud.settings import WS_PUBLIC_HOST +from webvirtcloud.settings import WS_PUBLIC_PORT +from webvirtcloud.settings import WS_PUBLIC_PATH from libvirt import libvirtError @@ -40,8 +42,9 @@ def console(request): console_websocket_port = None console_passwd = None - ws_port = console_websocket_port if console_websocket_port else WS_PORT + ws_port = console_websocket_port if console_websocket_port else WS_PUBLIC_PORT ws_host = WS_PUBLIC_HOST if WS_PUBLIC_HOST else request.get_host() + ws_path = WS_PUBLIC_PATH if WS_PUBLIC_PATH else '/' if ':' in ws_host: ws_host = re.sub(':[0-9]+', '', ws_host) diff --git a/create/forms.py b/create/forms.py index 0f208ce..d1e5bf0 100644 --- a/create/forms.py +++ b/create/forms.py @@ -38,6 +38,7 @@ class NewVMForm(forms.Form): disk = forms.IntegerField(required=False) memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')}) networks = forms.CharField(error_messages={'required': _('No Network pool has been choice')}) + nwfilter = forms.CharField(required=False) storage = forms.CharField(max_length=20, required=False) template = forms.CharField(required=False) images = forms.CharField(required=False) diff --git a/create/templates/create_instance.html b/create/templates/create_instance.html index 88b5fbe..8b741f2 100644 --- a/create/templates/create_instance.html +++ b/create/templates/create_instance.html @@ -107,6 +107,17 @@ {% endfor %} + +
    + +
    + +
    @@ -226,6 +237,17 @@
    +
    + +
    + +
    +
    @@ -379,6 +401,17 @@
    +
    + +
    + +
    +
    diff --git a/create/views.py b/create/views.py index 0c39a1d..2cacdc4 100644 --- a/create/views.py +++ b/create/views.py @@ -28,7 +28,6 @@ def create_instance(request, compute_id): storages = [] networks = [] meta_prealloc = False - #computes = Compute.objects.all() compute = get_object_or_404(Compute, pk=compute_id) flavors = Flavor.objects.filter().order_by('id') @@ -40,6 +39,7 @@ def create_instance(request, compute_id): storages = sorted(conn.get_storages(only_actives=True)) networks = sorted(conn.get_networks()) + nwfilters = conn.get_nwfilters() instances = conn.get_instances() videos = conn.get_video() cache_modes = sorted(conn.get_cache_modes().items()) @@ -139,11 +139,11 @@ def create_instance(request, compute_id): try: conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'], uuid, volumes, data['cache_mode'], data['networks'], data['virtio'], - data["listener_addr"], None, data["video"], data["console_pass"], + data["listener_addr"], data["nwfilter"], data["video"], data["console_pass"], data['mac']) create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid) create_instance.save() - messages.success(request,"Instance is created.") + messages.success(request, _("Instance is created.")) return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']])) except libvirtError as lib_err: if data['hdd_size'] or volumes[clone_path]: diff --git a/instances/templates/add_instance_network_block.html b/instances/templates/add_instance_network_block.html index 990c524..9d65601 100644 --- a/instances/templates/add_instance_network_block.html +++ b/instances/templates/add_instance_network_block.html @@ -33,12 +33,24 @@
    +
    + +
    + +
    + + diff --git a/instances/templates/compute_instances.html b/instances/templates/compute_instances.html new file mode 100644 index 0000000..e96c763 --- /dev/null +++ b/instances/templates/compute_instances.html @@ -0,0 +1,199 @@ +{% extends "base.html" %} +{% load i18n %} +{% load staticfiles %} +{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %} +{% block style %} + +{% endblock %} +{% block content %} + +
    +
    + {% if request.user.is_superuser %} + {% include 'create_inst_block.html' %} + {% endif %} + {% if all_host_vms or all_user_vms %} + + {% endif %} +

    {{ compute.name }}

    +
    +
    + + + + {% include 'errors_block.html' %} +
    + {% if not all_host_vms %} +
    +
    + + {% trans "Warning:" %} {% trans "Hypervisor doesn't have any instances" %} +
    +
    + {% else %} +
    + + + + + + + + + + + + + {% for host, inst in all_host_vms.items %} + {% for vm, info in inst.items %} + + + + + + + + + {% endfor %} + {% endfor %} + +
    Name
    Description
    Host
    User
    StatusVCPUMemoryActions
    {{ vm }}
    {{ info.title }}
    {{ host.1 }}
    {% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}
    + {% ifequal info.status 1 %}{% trans "Active" %}{% endifequal %} + {% ifequal info.status 5 %}{% trans "Off" %}{% endifequal %} + {% ifequal info.status 3 %}{% trans "Suspend" %}{% endifequal %} + {{ info.vcpu }}{{ info.memory|filesizeformat }}
    {% csrf_token %} + + + {% ifequal info.status 5 %} + {% if info.is_template %} + + {% else %} + + {% endif %} + + + + + {% endifequal %} + {% ifequal info.status 3 %} + + + + + + {% endifequal %} + {% ifequal info.status 1 %} + + + + + + + + {% endifequal %} +
    +
    +
    + {% endif %} +
    +{% endblock %} +{% block script %} + + + + +{% if request.user.is_superuser %} + +{% endif %} +{% endblock %} diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 9b34878..b61e73b 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -833,39 +833,63 @@ {% if request.user.is_superuser %}

    - {% trans "Assign network device to bridge" %} - {% include 'add_instance_network_block.html' %} + {% trans "Assign network device to bridge" %} + {% include 'add_instance_network_block.html' %}

    -
    {% csrf_token %} -

    {% trans "Network devices" %}

    +

    {% trans "Network devices" %}

    +
    + {% csrf_token %} {% for network in networks %} -
    - -
    - +
    +
    +
    -
    - +
    +
    + + + + +
    +
    + + + + +
    +
    + + + + +
    -
    - + +
    {% endfor %} - {% ifequal status 5 %} - - {% else %} - - {% endifequal %} + + + +
    +
    {% endif %} {% if request.user.is_superuser or request.user.userattributes.can_clone_instances %} @@ -1370,12 +1394,6 @@ }); }); - -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/networks/templates/network.html b/networks/templates/network.html index 7d922b1..085b35b 100644 --- a/networks/templates/network.html +++ b/networks/templates/network.html @@ -10,6 +10,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • @@ -19,6 +22,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • @@ -184,4 +190,4 @@ }); }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/networks/templates/networks.html b/networks/templates/networks.html index 36322e2..5e843ae 100644 --- a/networks/templates/networks.html +++ b/networks/templates/networks.html @@ -11,6 +11,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • @@ -20,6 +23,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • @@ -77,4 +83,4 @@ }).change(); }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/nwfilters/__init__.py b/nwfilters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nwfilters/admin.py b/nwfilters/admin.py new file mode 100644 index 0000000..13be29d --- /dev/null +++ b/nwfilters/admin.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin + +# Register your models here. diff --git a/nwfilters/apps.py b/nwfilters/apps.py new file mode 100644 index 0000000..11475c5 --- /dev/null +++ b/nwfilters/apps.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class NwfiltersConfig(AppConfig): + name = 'nwfilters' diff --git a/nwfilters/migrations/__init__.py b/nwfilters/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nwfilters/models.py b/nwfilters/models.py new file mode 100644 index 0000000..1dfab76 --- /dev/null +++ b/nwfilters/models.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models + +# Create your models here. diff --git a/nwfilters/templates/create_nwfilter_block.html b/nwfilters/templates/create_nwfilter_block.html new file mode 100644 index 0000000..01056ab --- /dev/null +++ b/nwfilters/templates/create_nwfilter_block.html @@ -0,0 +1,32 @@ +{% load i18n %} +{% if request.user.is_superuser %} + + + + + + +{% endif %} diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html new file mode 100644 index 0000000..9f97831 --- /dev/null +++ b/nwfilters/templates/nwfilter.html @@ -0,0 +1,220 @@ +{% extends "base.html" %} +{% load i18n %} +{% load staticfiles %} +{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %} +{% block content %} + +
    +
    + {% include 'create_nwfilter_block.html' %} +

    {% trans "NWFilter:" %} {{ name }}

    + +
    +
    + + + {% include 'errors_block.html' %} + {% include 'messages_block.html' %} + +
    +
    +

    {% trans "UUID:" %}

    +

    {% trans "Name:" %}

    +
    + +
    +

    {{ uuid }}

    +

    {{ name }}

    +
    + +
    +
    +

    {% trans "XML:" %}

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

    {% trans "Filter References:" %}

    +
    {% csrf_token %} +
    + + + + +
    +
    + +
    +
    +
    + + + + + + + + + + {% for ref in refs %} + + + + + + {% endfor %} + +
    #{% trans "Reference" %}{% trans "Action" %}
    {{ forloop.counter }}{{ ref }} +
    {% csrf_token %} + + +
    +
    +
    +
    +
    +

    {% trans "Rules:" %}

    + + + + + + +
    +
    +
    + + + + + + + + + + + + + + {% for rule in rules %} + + + + + + + + + + {% endfor %} + +
    {% trans "Rule" %}{% trans "ActionType" %}{% trans "Direction" %}{% trans "Priority" %}{% trans "Statematch" %}{% trans "Directives" %}{% trans "Action" %}
    {{ forloop.counter }}{{ rule.action }}{{ rule.direction }}{{ rule.priority }}{{ rule.statematch }}{{ rule.directives }} +
    {% csrf_token %} + + + + +
    +
    +
    +
    + + +{% endblock %} + +{% block script %} + + + + + +{% endblock %} diff --git a/nwfilters/templates/nwfilters.html b/nwfilters/templates/nwfilters.html new file mode 100644 index 0000000..9fd228f --- /dev/null +++ b/nwfilters/templates/nwfilters.html @@ -0,0 +1,171 @@ +{% extends "base.html" %} +{% load i18n %} +{% load staticfiles %} +{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %} +{% block content %} + +
    +
    + {% include 'create_nwfilter_block.html' %} +

    {{ compute.name }}

    + +
    +
    + + +{% include 'errors_block.html' %} +{% include 'messages_block.html' %} + +
    +
    +
    +
    + +
    + + +
    + {% if nwfilters %} +
    + + + + + + + + + + + {% for nwfilter in nwfilters %} + + + + + + + + + {% endfor %} + +
    #{% trans "UUID" %}{% trans "Name" %}{% trans "Action" %}
    {{ forloop.counter }}{{ nwfilter.uuid }}{{ nwfilter.name }} + + + + + + +
    {% csrf_token %} + + +
    +
    +
    + {% else %} +
    +
    + + {% trans "Warning:" %} {% trans "Hypervisor doesn't have any NWFilters" %} +
    +
    + {% endif %} +
    +
    +{% endblock %} + +{% block script %} + + + + + + + +{% endblock %} diff --git a/nwfilters/tests.py b/nwfilters/tests.py new file mode 100644 index 0000000..5982e6b --- /dev/null +++ b/nwfilters/tests.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.test import TestCase + +# Create your tests here. diff --git a/nwfilters/views.py b/nwfilters/views.py new file mode 100644 index 0000000..c0dd8d8 --- /dev/null +++ b/nwfilters/views.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.shortcuts import render +from django.http import HttpResponseRedirect +from django.shortcuts import render, get_object_or_404 +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse +from django.contrib.auth.decorators import login_required +from computes.models import Compute +from vrtManager import util +from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter +from vrtManager.instance import wvmInstances, wvmInstance +from libvirt import libvirtError +from logs.views import addlogmsg + + +@login_required +def nwfilters(request, compute_id): + """ + :param request: + :return: + """ + + if not request.user.is_superuser: + return HttpResponseRedirect(reverse('index')) + + error_messages = [] + nwfilters_all = [] + compute = get_object_or_404(Compute, pk=compute_id) + + try: + conn = wvmNWFilters(compute.hostname, + compute.login, + compute.password, + compute.type) + + if request.method == 'POST': + if 'create_nwfilter' in request.POST: + xml = request.POST.get('nwfilter_xml', '') + if xml: + try: + util.etree.fromstring(xml) + name = util.get_xml_path(xml, '/filter/@name') + uuid = util.get_xml_path(xml, '/filter/uuid') + except util.etree.ParseError: + name = None + + for nwf in nwfilters: + if name == nwf.name(): + error_msg = _("A network filter with this name already exists") + raise Exception(error_msg) + if uuid == nwf.UUIDString(): + error_msg = _("A network filter with this uuid already exists") + raise Exception(error_msg) + else: + try: + msg = _("Creating NWFilter: %s" % name) + conn.create_nwfilter(xml) + addlogmsg(request.user.username, compute.hostname, msg) + except libvirtError as lib_err: + error_messages.append(lib_err.message) + addlogmsg(request.user.username, compute.hostname, lib_err.message) + + if 'del_nwfilter' in request.POST: + name = request.POST.get('nwfiltername','') + msg = _("Deleting NWFilter: %s" % name) + in_use = False + nwfilter = conn.get_nwfilter(name) + + is_conn = wvmInstances(compute.hostname, compute.login, compute.password, compute.type) + instances = is_conn.get_instances() + for inst in instances: + # if in_use: break + i_conn = wvmInstance(compute.hostname, compute.login, compute.password, compute.type, inst) + dom_filterrefs = i_conn.get_filterrefs() + + if name in dom_filterrefs: + in_use = True + msg = _("NWFilter is in use by %s. Cannot be deleted." % inst) + error_messages.append(msg) + addlogmsg(request.user.username, compute.hostname, msg) + i_conn.close() + break + + is_conn.close() + if nwfilter and not in_use: + nwfilter.undefine() + addlogmsg(request.user.username, compute.hostname, msg) + + if 'cln_nwfilter' in request.POST: + + name = request.POST.get('nwfiltername','') + cln_name = request.POST.get('cln_name', name + '-clone') + + conn.clone_nwfilter(name,cln_name) + msg = _("Cloning NWFilter %s as %s" % (name, cln_name)) + addlogmsg(request.user.username, compute.hostname, msg) + + for nwf in conn.get_nwfilters(): + nwfilters_all.append(conn.get_nwfilter_info(nwf)) + + conn.close() + except libvirtError as lib_err: + error_messages.append(lib_err) + addlogmsg(request.user.username, compute.hostname, lib_err) + except Exception as err: + error_messages.append(err) + addlogmsg(request.user.username, compute.hostname, err) + + return render(request, 'nwfilters.html', {'error_messages': error_messages, + 'nwfilters': nwfilters_all, + 'compute': compute}) + + +@login_required +def nwfilter(request, compute_id, nwfltr): + + error_messages = [] + nwfilters_all = [] + compute = get_object_or_404(Compute, pk=compute_id) + + try: + nwfilter = wvmNWFilter(compute.hostname, + compute.login, + compute.password, + compute.type, + nwfltr) + conn = wvmNWFilters(compute.hostname, + compute.login, + compute.password, + compute.type) + + for nwf in conn.get_nwfilters(): + nwfilters_all.append(conn.get_nwfilter_info(nwf)) + + uuid = nwfilter.get_uuid() + name = nwfilter.get_name() + xml = nwfilter.get_xml() + rules = nwfilter.get_rules() + refs = nwfilter.get_filter_refs() + + if request.method == 'POST': + + if 'edit_nwfilter' in request.POST: + new_xml = request.POST.get('edit_xml', '') + + if new_xml: + nwfilter.delete() + try: + conn.create_nwfilter(new_xml) + except libvirtError as lib_err: + conn.create_nwfilter(xml) + raise libvirtError(lib_err) + + if 'del_nwfilter_rule' in request.POST: + action = request.POST.get('action', '') + direction = request.POST.get('direction', '') + priority = request.POST.get('priority', '') + + new_xml = nwfilter.delete_rule(action, direction, priority) + nwfilter.delete() + try: + conn.create_nwfilter(new_xml) + except libvirtError as lib_err: + conn.create_nwfilter(xml) + raise libvirtError(lib_err) + + if 'del_nwfilter_ref' in request.POST: + ref_name = request.POST.get('ref') + new_xml = nwfilter.delete_ref(ref_name) + nwfilter.delete() + try: + conn.create_nwfilter(new_xml) + except libvirtError as lib_err: + conn.create_nwfilter(xml) + raise libvirtError(lib_err) + + if 'add_nwfilter_rule' in request.POST: + rule_xml = request.POST.get('nwfilterrule_xml', '') + if not rule_xml: + return HttpResponseRedirect(request.get_full_path()) + new_xml = nwfilter.add_rule(rule_xml) + nwfilter.delete() + try: + conn.create_nwfilter(new_xml) + except libvirtError as lib_err: + conn.create_nwfilter(xml) + raise libvirtError(lib_err) + + if 'add_nwfilter_ref' in request.POST: + ref_name = request.POST.get('nwfilters_select', '') + if not ref_name: + return HttpResponseRedirect(request.get_full_path()) + new_xml = nwfilter.add_ref(ref_name) + nwfilter.delete() + try: + conn.create_nwfilter(new_xml) + except libvirtError as lib_err: + conn.create_nwfilter(xml) + raise libvirtError(lib_err) + + return HttpResponseRedirect(request.get_full_path()) + conn.close() + nwfilter.close() + except libvirtError as lib_err: + error_messages.append(lib_err) + except Exception as error_msg: + error_messages.append(error_msg) + + return render(request, 'nwfilter.html', locals()) \ No newline at end of file diff --git a/secrets/templates/secrets.html b/secrets/templates/secrets.html index 8286bfb..28d8dbd 100644 --- a/secrets/templates/secrets.html +++ b/secrets/templates/secrets.html @@ -10,11 +10,14 @@
    {% include 'create_secret_block.html' %} -

    {% trans "Secrets" %}

    +

    {{ compute.name }}

    diff --git a/storages/templates/storages.html b/storages/templates/storages.html index 21bca90..20f147c 100644 --- a/storages/templates/storages.html +++ b/storages/templates/storages.html @@ -11,6 +11,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • @@ -20,6 +23,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • @@ -64,4 +70,4 @@ {% endfor %} {% endif %}
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/storages/views.py b/storages/views.py index f18cad6..d2214aa 100644 --- a/storages/views.py +++ b/storages/views.py @@ -155,7 +155,7 @@ def storage(request, compute_id, pool): meta_prealloc = True try: conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc) - messages.success("Image file {} is created successfully".format(data['name']+".img")) + messages.success(request, _("Image file {} is created successfully".format(data['name']+".img"))) return HttpResponseRedirect(request.get_full_path()) except libvirtError as lib_err: error_messages.append(lib_err) @@ -167,7 +167,7 @@ def storage(request, compute_id, pool): try: vol = conn.get_volume(volname) vol.delete(0) - messages.success(request,_('Volume: {} is deleted.'.format(volname))) + messages.success(request, _('Volume: {} is deleted.'.format(volname))) return HttpResponseRedirect(request.get_full_path()) except libvirtError as lib_err: error_messages.append(lib_err.message) @@ -197,8 +197,7 @@ def storage(request, compute_id, pool): format = None try: conn.clone_volume(data['image'], data['name'], format, meta_prealloc) - messages.success(request, _("{} image cloned as {} successfully".format(data['image'], - data['name'] + ".img"))) + messages.success(request, _("{} image cloned as {} successfully".format(data['image'], data['name'] + ".img"))) return HttpResponseRedirect(request.get_full_path()) except libvirtError as lib_err: error_messages.append(lib_err) diff --git a/vrtManager/connection.py b/vrtManager/connection.py index 65c2915..1a16f4e 100644 --- a/vrtManager/connection.py +++ b/vrtManager/connection.py @@ -389,6 +389,12 @@ class wvmConnect(object): interface.append(inface) return interface + def get_nwfilters(self): + nwfilters = [] + for nwfilter in self.wvm.listNWFilters(): + nwfilters.append(nwfilter) + return nwfilters + def get_cache_modes(self): """Get cache available modes""" return { @@ -443,7 +449,6 @@ class wvmConnect(object): def get_video(self): """ Get available graphics video types """ - def get_video_list(ctx): result = [] for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'): @@ -470,6 +475,9 @@ class wvmConnect(object): def get_network(self, net): return self.wvm.networkLookupByName(net) + def get_nwfilter(self, name): + return self.wvm.nwfilterLookupByName(name) + def get_instance(self, name): return self.wvm.lookupByName(name) diff --git a/vrtManager/instance.py b/vrtManager/instance.py index b27baa0..d5bc1b9 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -187,6 +187,19 @@ class wvmInstance(wvmConnect): title = util.get_xml_path(self._XMLDesc(0), "/domain/title") return title if title else '' + def get_filterrefs(self): + + def filterrefs(ctx): + result = [] + for net in ctx.xpath('/domain/devices/interface'): + filterref = net.xpath('filterref/@filter') + if filterref: + result.append(filterref[0]) + return result + + return util.get_xml_path(self._XMLDesc(0), func=filterrefs) + + def get_description(self): description = util.get_xml_path(self._XMLDesc(0), "/domain/description") return description if description else '' @@ -219,12 +232,13 @@ class wvmInstance(wvmConnect): mac_host = net.xpath('mac/@address')[0] network_host = net.xpath('source/@network|source/@bridge|source/@dev')[0] target_host = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0] + filterref_host = '' if not net.xpath('filterref/@filter') else net.xpath('filterref/@filter')[0] try: net = self.get_network(network_host) ip = get_mac_ipaddr(net, mac_host) except libvirtError as e: ip = None - result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip}) + result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip, 'filterref': filterref_host}) return result return util.get_xml_path(self._XMLDesc(0), func=networks) @@ -487,8 +501,7 @@ class wvmInstance(wvmConnect): return self._defineXML(newxml) def get_console_socket(self): - socket = util.get_xml_path(self._XMLDesc(0), - "/domain/devices/graphics/@socket") + socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket") return socket def get_console_type(self): @@ -700,13 +713,11 @@ class wvmInstance(wvmConnect): source_file = elm.get('file') if source_file: clone_dev_path.append(source_file) - clone_path = os.path.join(os.path.dirname(source_file), - target_file) + clone_path = os.path.join(os.path.dirname(source_file), target_file) elm.set('file', clone_path) vol = self.get_volume_by_path(source_file) - vol_format = util.get_xml_path(vol.XMLDesc(0), - "/volume/target/format/@type") + vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type") if vol_format == 'qcow2' and meta_prealloc: meta_prealloc = True @@ -729,8 +740,7 @@ class wvmInstance(wvmConnect): elm.set('name', clone_name) vol = self.get_volume_by_path(source_name) - vol_format = util.get_xml_path(vol.XMLDesc(0), - "/volume/target/format/@type") + vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type") vol_clone_xml = """ @@ -776,7 +786,7 @@ class wvmInstance(wvmConnect): bridge_name = net.bridgeName() return bridge_name - def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio'): + def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio', nwfilter=None): tree = ElementTree.fromstring(self._XMLDesc(0)) bridge_name = self.get_bridge_name(source, source_type) xml_interface = """ @@ -784,8 +794,13 @@ class wvmInstance(wvmConnect): - - """ % (interface_type, mac_address, bridge_name, model) + """ % (interface_type, mac_address, bridge_name, model) + if nwfilter: + xml_interface += """ + + """ % nwfilter + xml_interface += """""" + if self.get_status() == 5: devices = tree.find('devices') elm_interface = ElementTree.fromstring(xml_interface) @@ -793,20 +808,42 @@ class wvmInstance(wvmConnect): xmldom = ElementTree.tostring(tree) self._defineXML(xmldom) + def delete_network(self, mac_address): + tree = ElementTree.fromstring(self._XMLDesc(0)) + devices = tree.find('devices') + for interface in tree.findall('devices/interface'): + source = interface.find('mac') + if source.get('address', '') == mac_address: + source = None + devices.remove(interface) + new_xml = ElementTree.tostring(tree) + self._defineXML(new_xml) + def change_network(self, network_data): xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE) tree = ElementTree.fromstring(xml) - for num, interface in enumerate(tree.findall('devices/interface')): net_source = network_data['net-source-' + str(num)] net_source_type = network_data['net-source-' + str(num) + '-type'] net_mac = network_data['net-mac-' + str(num)] + net_filter = network_data['net-nwfilter-' + str(num)] bridge_name = self.get_bridge_name(net_source, net_source_type) if interface.get('type') == 'bridge': source = interface.find('mac') source.set('address', net_mac) source = interface.find('source') source.set('bridge', bridge_name) + source = interface.find('filterref') + + if net_filter: + if source is not None: source.set('filter', net_filter) + else: + element = ElementTree.Element("filterref") + element.attrib['filter'] = net_filter + interface.append(element) + else: + if source is not None: interface.remove(source) + new_xml = ElementTree.tostring(tree) self._defineXML(new_xml) diff --git a/vrtManager/nwfilters.py b/vrtManager/nwfilters.py new file mode 100644 index 0000000..58784b9 --- /dev/null +++ b/vrtManager/nwfilters.py @@ -0,0 +1,125 @@ +from vrtManager.connection import wvmConnect +from xml.etree import ElementTree + + +class wvmNWFilters(wvmConnect): + def get_nwfilter_info(self, name): + nwfilter = self.get_nwfilter(name) + xml = nwfilter.XMLDesc(0) + uuid = nwfilter.UUIDString() + return {'name': name, 'uuid': uuid, 'xml': xml} + + def create_nwfilter(self, xml): + self.wvm.nwfilterDefineXML(xml) + + def clone_nwfilter(self,name, cln_name): + nwfilter = self.get_nwfilter(name) + if nwfilter: + tree = ElementTree.fromstring(nwfilter.XMLDesc(0)) + tree.set('name', cln_name) + uuid = tree.find('uuid') + tree.remove(uuid) + self.create_nwfilter(ElementTree.tostring(tree)) + + +class wvmNWFilter(wvmConnect): + def __init__(self, host, login, passwd, conn, nwfiltername): + wvmConnect.__init__(self, host, login, passwd, conn) + self.nwfilter = self.get_nwfilter(nwfiltername) + + def _XMLDesc(self, flags): + return self.nwfilter.XMLDesc(flags) + + def get_uuid(self): + return self.nwfilter.UUIDString() + + def get_name(self): + return self.nwfilter.name() + + def delete(self): + self.nwfilter.undefine() + + def get_xml(self): + tree = ElementTree.fromstring(self._XMLDesc(0)) + uuid = tree.find('uuid') + tree.remove(uuid) + return ElementTree.tostring(tree) + + def get_filter_refs(self): + refs = [] + tree = ElementTree.fromstring(self._XMLDesc(0)) + for ref in tree.findall("./filterref"): + refs.append(ref.get('filter')) + return refs + + def get_rules(self): + rules = [] + + tree = ElementTree.fromstring(self._XMLDesc(0)) + for r in tree.findall("./rule"): + rule_action = r.get('action') + rule_direction = r.get('direction') + rule_priority = r.get('priority') + rule_statematch = r.get('statematch') + + rule_directives = r.find("./") + if rule_directives is not None: + rule_directives = ElementTree.tostring(rule_directives) + + rule_info = { + "action": rule_action, + "direction": rule_direction, + "priority": rule_priority, + "statematch": rule_statematch, + "directives": rule_directives + } + + rules.append(rule_info) + + return rules + + def delete_ref(self, name): + tree = ElementTree.fromstring(self._XMLDesc(0)) + for ref in tree.findall("./filterref"): + if name == ref.get('filter'): + tree.remove(ref) + break + return ElementTree.tostring(tree) + + def delete_rule(self, action, direction, priority): + tree = ElementTree.fromstring(self._XMLDesc(0)) + + rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (action, direction, priority)) + if rule_tree: + tree.remove(rule_tree[0]) + + return ElementTree.tostring(tree) + + def add_ref(self, name): + tree = ElementTree.fromstring(self._XMLDesc(0)) + element = ElementTree.Element("filterref") + element.attrib['filter'] = name + tree.append(element) + return ElementTree.tostring(tree) + + def add_rule(self, xml): + tree = ElementTree.fromstring(self._XMLDesc(0)) + rule = ElementTree.fromstring(xml) + + rule_action = rule.get('action') + rule_direction = rule.get('direction') + rule_priority = rule.get('priority') + rule_directives = rule.find("./") + rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (rule_action, rule_direction, rule_priority)) + + if rule_tree: + rule_tree[0].append(rule_directives) + else: + element = ElementTree.Element("rule") + element.attrib['action'] = rule_action + element.attrib['direction'] = rule_direction + element.attrib['priority'] = rule_priority + element.append(rule_directives) + tree.append(element) + + return ElementTree.tostring(tree) diff --git a/vrtManager/storage.py b/vrtManager/storage.py index 7dc7515..21a1a7a 100644 --- a/vrtManager/storage.py +++ b/vrtManager/storage.py @@ -1,5 +1,6 @@ from vrtManager import util from vrtManager.connection import wvmConnect +from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner class wvmStorages(wvmConnect): @@ -205,7 +206,7 @@ class wvmStorage(wvmConnect): ) return vol_list - def create_volume(self, name, size, vol_fmt='qcow2', metadata=False): + def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, owner=owner): size = int(size) * 1073741824 storage_type = self.get_type() alloc = size @@ -222,8 +223,8 @@ class wvmStorage(wvmConnect): - 107 - 107 + %s + %s 0644 @@ -232,10 +233,10 @@ class wvmStorage(wvmConnect): - """ % (name, size, alloc, vol_fmt) + """ % (name, size, alloc, vol_fmt, owner, owner) self._createXML(xml, metadata) - def clone_volume(self, name, target_file, vol_fmt=None, metadata=False): + def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, owner=owner): storage_type = self.get_type() if storage_type == 'dir': target_file += '.img' @@ -250,8 +251,8 @@ class wvmStorage(wvmConnect): - 107 - 107 + %s + %s 0644 @@ -260,5 +261,5 @@ class wvmStorage(wvmConnect): - """ % (target_file, vol_fmt) + """ % (target_file, vol_fmt, owner,owner) self._createXMLFrom(xml, vol, metadata) diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index c75b1a2..7c78005 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -24,6 +24,7 @@ INSTALLED_APPS = ( 'networks', 'storages', 'interfaces', + 'nwfilters', 'instances', 'secrets', 'logs', @@ -105,6 +106,13 @@ WS_HOST = '0.0.0.0' # Websock public port WS_PUBLIC_HOST = None +# Websock public port - default is 80 - uncomment 443 and comment 80 if you want to use vnc over https +WS_PUBLIC_PORT = 80 +#WS_PUBLIC_PORT = 443 + +# Websock pubic path +WS_PUBLIC_PATH = '/novncd/' + # Websock SSL connection WS_CERT = None @@ -151,3 +159,5 @@ VIEW_INSTANCES_LIST_STYLE = 'grouped' INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2' INSTANCE_VOLUME_DEFAULT_BUS = 'virtio' INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync' +# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu) +INSTANCE_VOLUME_DEFAULT_OWNER = 0 diff --git a/webvirtcloud/urls.py b/webvirtcloud/urls.py index d169386..2aa2c58 100644 --- a/webvirtcloud/urls.py +++ b/webvirtcloud/urls.py @@ -7,6 +7,8 @@ from secrets.views import secrets from create.views import create_instance from interfaces.views import interfaces, interface from console.views import console +from nwfilters.views import nwfilters, nwfilter +from computes.views import computes # from django.contrib import admin urlpatterns = [ @@ -14,8 +16,13 @@ urlpatterns = [ url(r'^instances/$', instances, name='instances'), url(r'^instance/', include('instances.urls')), + url(r'^instances/$', instances, name='instances'), + url(r'^accounts/', include('accounts.urls')), + url(r'^computes/', include('computes.urls')), + url(r'^computes/', computes, name='computes'), + url(r'^logs/', include('logs.urls')), url(r'^datasource/', include('datasource.urls')), @@ -25,9 +32,12 @@ urlpatterns = [ url(r'^compute/(?P[0-9]+)/network/(?P[\w\-\.]+)/$', network, name='network'), url(r'^compute/(?P[0-9]+)/interfaces/$', interfaces, name='interfaces'), url(r'^compute/(?P[0-9]+)/interface/(?P[\w\-\.\:]+)/$', interface, name='interface'), + url(r'^compute/(?P[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'), + url(r'^compute/(?P[0-9]+)/nwfilter/(?P[\w\-\.\:]+)/$', nwfilter, name='nwfilter'), url(r'^compute/(?P[0-9]+)/secrets/$', secrets, name='secrets'), url(r'^compute/(?P[0-9]+)/create/$', create_instance, name='create_instance'), + url(r'^console/$', console, name='console'), # (r'^admin/', include(admin.site.urls)), ]