From 3f1acf09ef3f29d8c0cfa565d0cd069d890cfa65 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 21 Sep 2018 16:50:44 +0300 Subject: [PATCH 01/41] Add NWFilters --- computes/templates/overview.html | 3 + interfaces/templates/interface.html | 3 + interfaces/templates/interfaces.html | 3 + networks/templates/network.html | 3 + networks/templates/networks.html | 3 + nwfilters/__init__.py | 0 nwfilters/admin.py | 6 + nwfilters/apps.py | 8 + nwfilters/migrations/__init__.py | 0 nwfilters/models.py | 6 + .../templates/create_nwfilter_block.html | 32 +++ nwfilters/templates/nwfilter.html | 220 ++++++++++++++++++ nwfilters/templates/nwfilters.html | 168 +++++++++++++ nwfilters/tests.py | 6 + nwfilters/views.py | 183 +++++++++++++++ secrets/templates/secrets.html | 3 + storages/templates/storage.html | 3 + storages/templates/storages.html | 3 + vrtManager/connection.py | 10 +- vrtManager/instance.py | 3 +- vrtManager/nwfilters.py | 125 ++++++++++ webvirtcloud/urls.py | 4 + 22 files changed, 792 insertions(+), 3 deletions(-) create mode 100644 nwfilters/__init__.py create mode 100644 nwfilters/admin.py create mode 100644 nwfilters/apps.py create mode 100644 nwfilters/migrations/__init__.py create mode 100644 nwfilters/models.py create mode 100644 nwfilters/templates/create_nwfilter_block.html create mode 100644 nwfilters/templates/nwfilter.html create mode 100644 nwfilters/templates/nwfilters.html create mode 100644 nwfilters/tests.py create mode 100644 nwfilters/views.py create mode 100644 vrtManager/nwfilters.py diff --git a/computes/templates/overview.html b/computes/templates/overview.html index 34e68c5..89251cc 100644 --- a/computes/templates/overview.html +++ b/computes/templates/overview.html @@ -20,6 +20,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/interfaces/templates/interface.html b/interfaces/templates/interface.html index c55750d..014a44e 100644 --- a/interfaces/templates/interface.html +++ b/interfaces/templates/interface.html @@ -19,6 +19,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/interfaces/templates/interfaces.html b/interfaces/templates/interfaces.html index 42d4456..58d4978 100644 --- a/interfaces/templates/interfaces.html +++ b/interfaces/templates/interfaces.html @@ -21,6 +21,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/networks/templates/network.html b/networks/templates/network.html index 7d922b1..e3b0275 100644 --- a/networks/templates/network.html +++ b/networks/templates/network.html @@ -19,6 +19,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/networks/templates/networks.html b/networks/templates/networks.html index 36322e2..2acc1f3 100644 --- a/networks/templates/networks.html +++ b/networks/templates/networks.html @@ -20,6 +20,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • 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..1f7c7b0 --- /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..e237380 --- /dev/null +++ b/nwfilters/templates/nwfilters.html @@ -0,0 +1,168 @@ +{% 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..5fa39f5 --- /dev/null +++ b/nwfilters/views.py @@ -0,0 +1,183 @@ +# -*- 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 libvirt import libvirtError + + +@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) + + for nwf in conn.get_nwfilters(): + nwfilters_all.append(conn.get_nwfilter_info(nwf.name())) + + 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: + conn.create_nwfilter(xml) + return HttpResponseRedirect(request.get_full_path()) + except libvirtError as lib_err: + error_messages.append(lib_err.message) + if 'del_nwfilter' in request.POST: + name = request.POST.get('nwfiltername','') + nwfilter = conn.get_nwfilter(name) + if nwfilter: + nwfilter.undefine() + return HttpResponseRedirect(request.get_full_path()) + + 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) + return HttpResponseRedirect(request.get_full_path()) + + conn.close() + except libvirtError as lib_err: + error_messages.append(lib_err) + except Exception as err: + error_messages.append(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.name())) + + 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..07b104a 100644 --- a/secrets/templates/secrets.html +++ b/secrets/templates/secrets.html @@ -24,6 +24,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/storages/templates/storage.html b/storages/templates/storage.html index ecc9adc..4d3c706 100644 --- a/storages/templates/storage.html +++ b/storages/templates/storage.html @@ -25,6 +25,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/storages/templates/storages.html b/storages/templates/storages.html index 21bca90..026fe13 100644 --- a/storages/templates/storages.html +++ b/storages/templates/storages.html @@ -20,6 +20,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/vrtManager/connection.py b/vrtManager/connection.py index 65c2915..96a4fbf 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.listAllNWFilters(): + 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..d63d36c 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -487,8 +487,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): 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/webvirtcloud/urls.py b/webvirtcloud/urls.py index d169386..79992af 100644 --- a/webvirtcloud/urls.py +++ b/webvirtcloud/urls.py @@ -7,6 +7,7 @@ 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 django.contrib import admin urlpatterns = [ @@ -25,9 +26,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)), ] From 1802ad0413df2a009ba7bd23c78223fa726bcdd8 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 21 Sep 2018 18:46:33 +0300 Subject: [PATCH 02/41] Check usage of filter refs from domains. --- nwfilters/templates/nwfilter.html | 2 +- nwfilters/views.py | 42 +++++++++++++++++++++++++------ vrtManager/instance.py | 13 ++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html index 1f7c7b0..0d2e2f1 100644 --- a/nwfilters/templates/nwfilter.html +++ b/nwfilters/templates/nwfilter.html @@ -65,7 +65,7 @@
    + +
    + + + + + + + +{% endif %} diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html new file mode 100644 index 0000000..1f7c7b0 --- /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..e237380 --- /dev/null +++ b/nwfilters/templates/nwfilters.html @@ -0,0 +1,168 @@ +{% 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..5fa39f5 --- /dev/null +++ b/nwfilters/views.py @@ -0,0 +1,183 @@ +# -*- 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 libvirt import libvirtError + + +@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) + + for nwf in conn.get_nwfilters(): + nwfilters_all.append(conn.get_nwfilter_info(nwf.name())) + + 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: + conn.create_nwfilter(xml) + return HttpResponseRedirect(request.get_full_path()) + except libvirtError as lib_err: + error_messages.append(lib_err.message) + if 'del_nwfilter' in request.POST: + name = request.POST.get('nwfiltername','') + nwfilter = conn.get_nwfilter(name) + if nwfilter: + nwfilter.undefine() + return HttpResponseRedirect(request.get_full_path()) + + 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) + return HttpResponseRedirect(request.get_full_path()) + + conn.close() + except libvirtError as lib_err: + error_messages.append(lib_err) + except Exception as err: + error_messages.append(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.name())) + + 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..07b104a 100644 --- a/secrets/templates/secrets.html +++ b/secrets/templates/secrets.html @@ -24,6 +24,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/storages/templates/storage.html b/storages/templates/storage.html index ecc9adc..4d3c706 100644 --- a/storages/templates/storage.html +++ b/storages/templates/storage.html @@ -25,6 +25,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/storages/templates/storages.html b/storages/templates/storages.html index 21bca90..026fe13 100644 --- a/storages/templates/storages.html +++ b/storages/templates/storages.html @@ -20,6 +20,9 @@
  • {% trans "Interfaces" %}
  • +
  • + {% trans "NWFilters" %} +
  • {% trans "Secrets" %}
  • diff --git a/vrtManager/connection.py b/vrtManager/connection.py index 65c2915..96a4fbf 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.listAllNWFilters(): + 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..d63d36c 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -487,8 +487,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): 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/webvirtcloud/urls.py b/webvirtcloud/urls.py index d169386..79992af 100644 --- a/webvirtcloud/urls.py +++ b/webvirtcloud/urls.py @@ -7,6 +7,7 @@ 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 django.contrib import admin urlpatterns = [ @@ -25,9 +26,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)), ] From 7f2104c19d30f331214943fdbaf34a7237064837 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 21 Sep 2018 18:46:33 +0300 Subject: [PATCH 04/41] Check usage of filter refs from domains. --- nwfilters/templates/nwfilter.html | 2 +- nwfilters/views.py | 42 +++++++++++++++++++++++++------ vrtManager/instance.py | 13 ++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html index 1f7c7b0..0d2e2f1 100644 --- a/nwfilters/templates/nwfilter.html +++ b/nwfilters/templates/nwfilter.html @@ -65,7 +65,7 @@
    + +
    + +
    + +
    @@ -226,6 +237,17 @@
    +
    + +
    + +
    +
    @@ -379,6 +401,17 @@
    +
    + +
    + +
    +
    diff --git a/create/views.py b/create/views.py index 0c39a1d..9fb846e 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,7 +139,7 @@ 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() diff --git a/instances/templates/add_instance_network_block.html b/instances/templates/add_instance_network_block.html index 990c524..afb0ae5 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/instance.html b/instances/templates/instance.html index 9b34878..4dbfa6d 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -859,11 +859,8 @@ {% endfor %} - {% ifequal status 5 %} - - {% else %} - - {% endifequal %} + +
    diff --git a/instances/views.py b/instances/views.py index ec6adbc..0f4469f 100644 --- a/instances/views.py +++ b/instances/views.py @@ -364,6 +364,7 @@ def instance(request, compute_id, vname): else: media_iso = [] networks = conn.get_net_device() + nwfilters = conn.get_nwfilters() vcpu_range = conn.get_max_cpus() memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384] if memory not in memory_range: @@ -708,9 +709,10 @@ def instance(request, compute_id, vname): if 'add_network' in request.POST: mac = request.POST.get('add-net-mac') + nwfilter = request.POST.get('nwfilter') (source, source_type) = get_network_tuple(request.POST.get('add-net-network')) - conn.add_network(mac, source, source_type) + conn.add_network(mac, source, source_type, nwfilter=nwfilter) msg = _("Edit network") addlogmsg(request.user.username, instance.name, msg) msg = _("Network Devices are changed. Please reboot instance to activate.") diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html index 0d2e2f1..9f97831 100644 --- a/nwfilters/templates/nwfilter.html +++ b/nwfilters/templates/nwfilter.html @@ -22,7 +22,7 @@ {% trans "Interfaces" %}
  • - {% trans "NWFilters" %} + {% trans "NWFilters" %}
  • {% trans "Secrets" %} diff --git a/nwfilters/views.py b/nwfilters/views.py index 99686ed..c0dd8d8 100644 --- a/nwfilters/views.py +++ b/nwfilters/views.py @@ -98,7 +98,7 @@ def nwfilters(request, compute_id): addlogmsg(request.user.username, compute.hostname, msg) for nwf in conn.get_nwfilters(): - nwfilters_all.append(conn.get_nwfilter_info(nwf.name())) + nwfilters_all.append(conn.get_nwfilter_info(nwf)) conn.close() except libvirtError as lib_err: @@ -132,7 +132,7 @@ def nwfilter(request, compute_id, nwfltr): compute.type) for nwf in conn.get_nwfilters(): - nwfilters_all.append(conn.get_nwfilter_info(nwf.name())) + nwfilters_all.append(conn.get_nwfilter_info(nwf)) uuid = nwfilter.get_uuid() name = nwfilter.get_name() diff --git a/vrtManager/connection.py b/vrtManager/connection.py index 96a4fbf..1a16f4e 100644 --- a/vrtManager/connection.py +++ b/vrtManager/connection.py @@ -391,7 +391,7 @@ class wvmConnect(object): def get_nwfilters(self): nwfilters = [] - for nwfilter in self.wvm.listAllNWFilters(): + for nwfilter in self.wvm.listNWFilters(): nwfilters.append(nwfilter) return nwfilters diff --git a/vrtManager/instance.py b/vrtManager/instance.py index ca5568f..dd2db64 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -788,7 +788,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 = """ @@ -796,8 +796,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) From 812bbb2ccc5ff22926ce322e5c5f9c784e1c86e4 Mon Sep 17 00:00:00 2001 From: "Ing. Jan KRCMAR" Date: Tue, 25 Sep 2018 23:25:06 +0200 Subject: [PATCH 07/41] console-spice-full fix scheme wss when accessing from https --- console/templates/console-spice-full.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/console/templates/console-spice-full.html b/console/templates/console-spice-full.html index 4bf75af..5accf4d 100644 --- a/console/templates/console-spice-full.html +++ b/console/templates/console-spice-full.html @@ -100,6 +100,9 @@ host = document.getElementById("host").value; port = document.getElementById("port").value; + if (window.location.protocol == 'https:') { + scheme = "wss://"; + } password = document.getElementById("password").value; From cdb5c4f4128852aa06b3485a47c33e930ee1fba6 Mon Sep 17 00:00:00 2001 From: "Ing. Jan KRCMAR" Date: Wed, 26 Sep 2018 09:53:23 +0200 Subject: [PATCH 08/41] fix wvmInterface get_ipv4/6: xml_path should specify protocol family --- vrtManager/interface.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vrtManager/interface.py b/vrtManager/interface.py index 9d04630..c91aaf9 100644 --- a/vrtManager/interface.py +++ b/vrtManager/interface.py @@ -81,7 +81,7 @@ class wvmInterface(wvmConnect): def get_ipv4_type(self): try: xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) - ipaddr = util.get_xml_path(xml, "/interface/protocol/ip/@address") + ipaddr = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@address") if ipaddr: return 'static' else: @@ -91,8 +91,8 @@ class wvmInterface(wvmConnect): def get_ipv4(self): xml = self._XMLDesc() - int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol/ip/@address") - int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol/ip/@prefix") + int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@address") + int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@prefix") if not int_ipv4_ip or not int_ipv4_mask: return None else: @@ -101,7 +101,7 @@ class wvmInterface(wvmConnect): def get_ipv6_type(self): try: xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) - ipaddr = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") + ipaddr = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@address") if ipaddr: return 'static' else: @@ -111,8 +111,8 @@ class wvmInterface(wvmConnect): def get_ipv6(self): xml = self._XMLDesc() - int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") - int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[2]/ip/@prefix") + int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@address") + int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@prefix") if not int_ipv6_ip or not int_ipv6_mask: return None else: From f7d2d24d0bca6bbe2fa649182f0cec0e47a75b8c Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 26 Sep 2018 16:19:32 +0300 Subject: [PATCH 09/41] message.success fix - (request was missing) --- storages/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storages/views.py b/storages/views.py index f18cad6..71bee86 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) From 5d4a6009082bd6218f6a582838118be63cc5c0f5 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 26 Sep 2018 17:16:48 +0300 Subject: [PATCH 10/41] success message converted to translate ready --- create/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create/views.py b/create/views.py index 9fb846e..2cacdc4 100644 --- a/create/views.py +++ b/create/views.py @@ -143,7 +143,7 @@ def create_instance(request, compute_id): 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]: From ba212971faec023950169d14e197e24ad226029f Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 26 Sep 2018 17:18:30 +0300 Subject: [PATCH 11/41] success message converted to translate ready --- storages/views.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/storages/views.py b/storages/views.py index 71bee86..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(request, "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) From b916c9dcf916376209efdd66e971b465de94a286 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 26 Sep 2018 17:20:46 +0300 Subject: [PATCH 12/41] instance network tab modified. Changing function modified. deleting function added. network info of nwfilters added. and some small fixes applied --- .../templates/add_instance_network_block.html | 4 +- instances/templates/instance.html | 75 ++++++++++++------- instances/views.py | 27 ++++--- vrtManager/instance.py | 27 ++++++- 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/instances/templates/add_instance_network_block.html b/instances/templates/add_instance_network_block.html index afb0ae5..9d65601 100644 --- a/instances/templates/add_instance_network_block.html +++ b/instances/templates/add_instance_network_block.html @@ -36,9 +36,9 @@
    - - {% for nwfilter in nwfilters %} + {% for nwfilter in compute_nwfilters %} {% endfor %} diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 4dbfa6d..b61e73b 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -833,36 +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 %} - - + + + +
    +
    {% endif %} {% if request.user.is_superuser or request.user.userattributes.can_clone_instances %} @@ -1367,12 +1394,6 @@ }); }); - + + + +{% if request.user.is_superuser %} + +{% endif %} +{% endblock %} diff --git a/instances/templates/instances_grouped.html b/instances/templates/allinstances_index_grouped.html similarity index 100% rename from instances/templates/instances_grouped.html rename to instances/templates/allinstances_index_grouped.html diff --git a/instances/templates/instances_nongrouped.html b/instances/templates/allinstances_index_nongrouped.html similarity index 100% rename from instances/templates/instances_nongrouped.html rename to instances/templates/allinstances_index_nongrouped.html diff --git a/instances/templates/instances.html b/instances/templates/instances.html index 6750603..7edb9da 100644 --- a/instances/templates/instances.html +++ b/instances/templates/instances.html @@ -1,12 +1,12 @@ {% extends "base.html" %} {% load i18n %} {% load staticfiles %} -{% block title %}{% trans "Instances" %}{% endblock %} +{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %} {% block style %} {% endblock %} {% block content %} - +
    {% if request.user.is_superuser %} @@ -17,129 +17,144 @@
    {% endif %} -

    {% trans "Instances" %}

    +

    {{ compute.name }}

    - +
    +
    - {% include 'errors_block.html' %} + +
    +
    + -
    + {% include 'errors_block.html' %} +
    + {% if not all_host_vms %}
    -
    - {% if request.user.is_superuser %} - {% if not all_host_vms %} -
    -
    - - {% trans "Warning:" %} {% trans "You don't have any Instance" %} -
    -
    - {% else %} - {% ifequal view_style "nongrouped" %} - {% include 'instances_nongrouped.html' %} - {% endifequal %} - {% ifequal view_style "grouped" %} - {% include 'instances_grouped.html' %} - {% endifequal %} - {% endif %} - {% else %} - {% if not all_user_vms %} -
    -
    - - {% trans "Warning:" %} {% trans "You don't have any Instance" %} -
    -
    - {% else %} - - - - - - - - - - - - {% for inst, vm in all_user_vms.items %} - - - - - - - - {% endfor %} - -
    NameStatusVCPUMemoryActions
    {{ vm.name }}
    {{ vm.title }}
    {% ifequal vm.status 1 %} - {% trans "Active" %} - {% endifequal %} - {% ifequal vm.status 5 %} - {% trans "Off" %} - {% endifequal %} - {% ifequal vm.status 3 %} - {% trans "Suspend" %} - {% endifequal %} - {{ vm.vcpu }}{{ vm.memory }} {% trans "MB" %}
    {% csrf_token %} - - - {% ifequal vm.status 5 %} - {% if inst.instance.is_template %} - - {% else %} - - {% endif %} - - - - {% endifequal %} - {% ifequal vm.status 3 %} - - - - - {% endifequal %} - {% ifequal vm.status 1 %} - - - - - - - {% endifequal %} -
    -
    - {% endif %} - {% endif %} +
    + + {% 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 %} @@ -181,4 +196,4 @@ } {% endif %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/instances/urls.py b/instances/urls.py index 93ac0c4..1054ed1 100644 --- a/instances/urls.py +++ b/instances/urls.py @@ -2,6 +2,7 @@ from django.conf.urls import url from . import views urlpatterns = [ + url(r'^$', views.allinstances, name='allinstances'), url(r'^(?P[0-9]+)/(?P[\w\-\.]+)/$', views.instance, name='instance'), url(r'^statistics/(?P[0-9]+)/(?P[\w\-\.]+)/$', views.inst_graph, name='inst_graph'), url(r'^status/(?P[0-9]+)/(?P[\w\-\.]+)/$', views.inst_status, name='inst_status'), diff --git a/instances/views.py b/instances/views.py index 70fb9bf..188b8e5 100644 --- a/instances/views.py +++ b/instances/views.py @@ -26,175 +26,72 @@ from logs.views import addlogmsg from django.conf import settings from django.contrib import messages - @login_required def index(request): """ :param request: :return: """ - - return HttpResponseRedirect(reverse('instances')) + return HttpResponseRedirect(reverse('allinstances')) @login_required -def instances(request): +def allinstances(request): + """ + INSTANCES LIST FOR ALL HOSTS + :param request: + :return: + """ + all_host_vms = {} + error_messages = [] + computes = Compute.objects.all().order_by("name") + + if not request.user.is_superuser: + all_user_vms = get_user_instances(request) + else: + for comp in computes: + try: + all_host_vms.update(get_host_instances(request,comp)) + except libvirtError as lib_err: + error_messages.append(lib_err) + + if request.method == 'POST': + try: + instances_actions(request) + except libvirtError as lib_err: + error_messages.append(lib_err) + addlogmsg(request.user.username, instance.name, lib_err.message) + + view_style = settings.VIEW_INSTANCES_LIST_STYLE + + return render(request, 'allinstances.html', locals()) + + +@login_required +def instances(request, compute_id): """ :param request: :return: """ error_messages = [] - all_host_vms = {} - all_user_vms = {} - computes = Compute.objects.all().order_by("name") + compute = get_object_or_404(Compute, pk=compute_id) - def get_userinstances_info(instance): - info = {} - uis = UserInstance.objects.filter(instance=instance) - info['count'] = uis.count() - if info['count'] > 0: - info['first_user'] = uis[0] - else: - info['first_user'] = None - return info - - def refresh_instance_database(comp, vm, info): - instances = Instance.objects.filter(name=vm) - if instances.count() > 1: - for i in instances: - user_instances_count = UserInstance.objects.filter(instance=i).count() - if user_instances_count == 0: - addlogmsg(request.user.username, i.name, _("Deleting due to multiple records.")) - i.delete() - - try: - check_uuid = Instance.objects.get(compute_id=comp["id"], name=vm) - if check_uuid.uuid != info['uuid']: - check_uuid.save() - - all_host_vms[comp["id"], - comp["name"], - comp["status"], - comp["cpu"], - comp["mem_size"], - comp["mem_perc"]][vm]['is_template'] = check_uuid.is_template - all_host_vms[comp["id"], - comp["name"], - comp["status"], - comp["cpu"], - comp["mem_size"], - comp["mem_perc"]][vm]['userinstances'] = get_userinstances_info(check_uuid) - except Instance.DoesNotExist: - check_uuid = Instance(compute_id=comp["id"], name=vm, uuid=info['uuid']) - check_uuid.save() - if not request.user.is_superuser: - user_instances = UserInstance.objects.filter(user_id=request.user.id) - for usr_inst in user_instances: - if connection_manager.host_is_up(usr_inst.instance.compute.type, - usr_inst.instance.compute.hostname): - conn = wvmHostDetails(usr_inst.instance.compute, - usr_inst.instance.compute.login, - usr_inst.instance.compute.password, - usr_inst.instance.compute.type) - all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name) - all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id}) + all_user_vms = get_user_instances(request) else: - for comp in computes: - status = connection_manager.host_is_up(comp.type, comp.hostname) - if status: - try: - conn = wvmHostDetails(comp, comp.login, comp.password, comp.type) - comp_node_info = conn.get_node_info() - comp_mem = conn.get_memory_usage() - comp_instances = conn.get_host_instances(True) - - if comp_instances: - comp_info= { - "id": comp.id, - "name": comp.name, - "status": status, - "cpu": comp_node_info[3], - "mem_size": comp_node_info[2], - "mem_perc": comp_mem['percent'] - } - all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"], - comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances - for vm, info in comp_instances.items(): - refresh_instance_database(comp_info, vm, info) - - conn.close() - except libvirtError as lib_err: - error_messages.append(lib_err) + try: + all_host_vms = get_host_instances(request, compute) + except libvirtError as lib_err: + error_messages.append(lib_err) if request.method == 'POST': - name = request.POST.get('name', '') - compute_id = request.POST.get('compute_id', '') - instance = Instance.objects.get(compute_id=compute_id, name=name) try: - conn = wvmInstances(instance.compute.hostname, - instance.compute.login, - instance.compute.password, - instance.compute.type) - if 'poweron' in request.POST: - msg = _("Power On") - addlogmsg(request.user.username, instance.name, msg) - conn.start(name) - return HttpResponseRedirect(request.get_full_path()) - - if 'poweroff' in request.POST: - msg = _("Power Off") - addlogmsg(request.user.username, instance.name, msg) - conn.shutdown(name) - return HttpResponseRedirect(request.get_full_path()) - - if 'powercycle' in request.POST: - msg = _("Power Cycle") - conn.force_shutdown(name) - conn.start(name) - addlogmsg(request.user.username, instance.name, msg) - return HttpResponseRedirect(request.get_full_path()) - - if 'getvvfile' in request.POST: - msg = _("Send console.vv file") - addlogmsg(request.user.username, instance.name, msg) - response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None, charset='utf-8') - response.writelines('[virt-viewer]\n') - response.writelines('type=' + conn.graphics_type(name) + '\n') - response.writelines('host=' + conn.graphics_listen(name) + '\n') - response.writelines('port=' + conn.graphics_port(name) + '\n') - response.writelines('title=' + conn.domain_name(name) + '\n') - response.writelines('password=' + conn.graphics_passwd(name) + '\n') - response.writelines('enable-usbredir=1\n') - response.writelines('disable-effects=all\n') - response.writelines('secure-attention=ctrl+alt+ins\n') - response.writelines('release-cursor=ctrl+alt\n') - response.writelines('fullscreen=1\n') - response.writelines('delete-this-file=1\n') - response['Content-Disposition'] = 'attachment; filename="console.vv"' - return response - - if request.user.is_superuser: - - if 'suspend' in request.POST: - msg = _("Suspend") - addlogmsg(request.user.username, instance.name, msg) - conn.suspend(name) - return HttpResponseRedirect(request.get_full_path()) - - if 'resume' in request.POST: - msg = _("Resume") - addlogmsg(request.user.username, instance.name, msg) - conn.resume(name) - return HttpResponseRedirect(request.get_full_path()) - + instances_actions(request) except libvirtError as lib_err: error_messages.append(lib_err) addlogmsg(request.user.username, instance.name, lib_err.message) - view_style = settings.VIEW_INSTANCES_LIST_STYLE - return render(request, 'instances.html', locals()) @@ -206,7 +103,7 @@ def instance(request, compute_id, vname): """ error_messages = [] - #messages = [] + # messages = [] compute = get_object_or_404(Compute, pk=compute_id) computes = Compute.objects.all().order_by('name') computes_count = computes.count() @@ -217,8 +114,8 @@ def instance(request, compute_id, vname): console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES try: userinstance = UserInstance.objects.get(instance__compute_id=compute_id, - instance__name=vname, - user__id=request.user.id) + instance__name=vname, + user__id=request.user.id) except UserInstance.DoesNotExist: userinstance = None @@ -231,15 +128,15 @@ def instance(request, compute_id, vname): return 0 size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B") if 'K' == size_str[-1]: - return long(float(size_str[:-1]))<<10 + return long(float(size_str[:-1])) << 10 elif 'M' == size_str[-1]: - return long(float(size_str[:-1]))<<20 + return long(float(size_str[:-1])) << 20 elif 'G' == size_str[-1]: - return long(float(size_str[:-1]))<<30 + return long(float(size_str[:-1])) << 30 elif 'T' == size_str[-1]: - return long(float(size_str[:-1]))<<40 + return long(float(size_str[:-1])) << 40 elif 'P' == size_str[-1]: - return long(float(size_str[:-1]))<<50 + return long(float(size_str[:-1])) << 50 else: return long(float(size_str)) @@ -268,16 +165,16 @@ def instance(request, compute_id, vname): if connection_manager.host_is_up(usr_inst.instance.compute.type, usr_inst.instance.compute.hostname): conn = wvmInstance(usr_inst.instance.compute, - usr_inst.instance.compute.login, - usr_inst.instance.compute.password, - usr_inst.instance.compute.type, - usr_inst.instance.name) + usr_inst.instance.compute.login, + usr_inst.instance.compute.password, + usr_inst.instance.compute.type, + usr_inst.instance.name) cpu += int(conn.get_vcpu()) memory += int(conn.get_memory()) for disk in conn.get_disk_device(): if disk['size']: - disk_size += int(disk['size'])>>30 - + disk_size += int(disk['size']) >> 30 + if ua.max_instances > 0 and instance > ua.max_instances: msg = "instance" if settings.QUOTA_DEBUG: @@ -301,7 +198,7 @@ def instance(request, compute_id, vname): dev_base = "vd" else: dev_base = "sd" - existing_devs = [ disk['dev'] for disk in disks ] + existing_devs = [disk['dev'] for disk in disks] for l in string.lowercase: dev = dev_base + l if dev not in existing_devs: @@ -379,7 +276,7 @@ def instance(request, compute_id, vname): console_port = conn.get_console_port() console_keymap = conn.get_console_keymap() console_listen_address = conn.get_console_listen_addr() - snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k:k['date']) + snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k: k['date']) inst_xml = conn._XMLDesc(VIR_DOMAIN_XML_SECURE) has_managed_save_image = conn.get_managed_save_image() console_passwd = conn.get_console_passwd() @@ -453,7 +350,8 @@ def instance(request, compute_id, vname): instance.delete() try: - del_userinstance = UserInstance.objects.filter(instance__compute_id=compute_id, instance__name=vname) + del_userinstance = UserInstance.objects.filter(instance__compute_id=compute_id, + instance__name=vname) del_userinstance.delete() except UserInstance.DoesNotExist: pass @@ -507,7 +405,8 @@ def instance(request, compute_id, vname): msg = _("Please shutdown down your instance and then try again") error_messages.append(msg) - if 'resize' in request.POST and (request.user.is_superuser or request.user.is_staff or userinstance.is_change): + if 'resize' in request.POST and ( + request.user.is_superuser or request.user.is_staff or userinstance.is_change): new_vcpu = request.POST.get('vcpu', '') new_cur_vcpu = request.POST.get('cur_vcpu', '') new_memory = request.POST.get('memory', '') @@ -521,13 +420,13 @@ def instance(request, compute_id, vname): disks_new = [] for disk in disks: input_disk_size = filesizefstr(request.POST.get('disk_size_' + disk['dev'], '')) - if input_disk_size > disk['size']+(64<<20): + if input_disk_size > disk['size'] + (64 << 20): disk['size_new'] = input_disk_size - disks_new.append(disk) - disk_sum = sum([disk['size']>>30 for disk in disks_new]) - disk_new_sum = sum([disk['size_new']>>30 for disk in disks_new]) - quota_msg = check_user_quota(0, int(new_vcpu)-vcpu, int(new_memory)-memory, disk_new_sum-disk_sum) - if not request.user.is_superuser and quota_msg: + disks_new.append(disk) + disk_sum = sum([disk['size'] >> 30 for disk in disks_new]) + disk_new_sum = sum([disk['size_new'] >> 30 for disk in disks_new]) + quota_msg = check_user_quota(0, int(new_vcpu) - vcpu, int(new_memory) - memory, disk_new_sum - disk_sum) + if not request.user.is_superuser and quota_msg: msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name)) error_messages.append(msg) else: @@ -542,9 +441,9 @@ def instance(request, compute_id, vname): if 'addvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): connCreate = wvmCreate(compute.hostname, - compute.login, - compute.password, - compute.type) + compute.login, + compute.password, + compute.type) storage = request.POST.get('storage', '') name = request.POST.get('name', '') format = request.POST.get('format', default_format) @@ -553,7 +452,7 @@ def instance(request, compute_id, vname): bus = request.POST.get('bus', default_bus) cache = request.POST.get('cache', default_cache) target = get_new_disk_dev(disks, bus) - + path = connCreate.create_volume(storage, name, size, format, meta_prealloc, default_owner) conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus) msg = _('Attach new disk') @@ -670,7 +569,7 @@ 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) @@ -705,7 +604,8 @@ def instance(request, compute_id, vname): conn.change_network(network_data) addlogmsg(request.user.username, instance.name, msg) - if conn.get_status() != 5: messages.success(request, _("Network Devices are changed. Please reboot instance to activate.")) + msg = _("Network Devices are changed. Please reboot instance to activate.") + if conn.get_status() != 5: messages.success(request, msg) return HttpResponseRedirect(request.get_full_path() + '#network') if 'add_network' in request.POST: @@ -717,7 +617,9 @@ def instance(request, compute_id, vname): conn.add_network(mac, source, source_type, nwfilter=nwfilter) addlogmsg(request.user.username, instance.name, msg) - if conn.get_status() != 5: messages.success(request, _("Network Device is added. Please reboot instance to activate.")) + msg = _("Network Device is added. Please reboot instance to activate.") + if conn.get_status() != 5: messages.success(request, msg) + return HttpResponseRedirect(request.get_full_path() + '#network') if 'delete_network' in request.POST: @@ -726,17 +628,18 @@ def instance(request, compute_id, vname): conn.delete_network(mac_address) addlogmsg(request.user.username, instance.name, msg) - if conn.get_status() != 5: messages.success(request, _("Network Device is deleted. Please reboot instance to activate.")) + msg = _("Network Device is deleted. Please reboot instance to activate.") + if conn.get_status() != 5: messages.success(request, msg) return HttpResponseRedirect(request.get_full_path() + '#network') if 'add_owner' in request.POST: user_id = int(request.POST.get('user_id', '')) - + if settings.ALLOW_INSTANCE_MULTIPLE_OWNER: check_inst = UserInstance.objects.filter(instance=instance, user_id=user_id) else: check_inst = UserInstance.objects.filter(instance=instance) - + if check_inst: msg = _("Owner already added") error_messages.append(msg) @@ -755,19 +658,18 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#users') - if request.user.is_superuser or request.user.userattributes.can_clone_instances: if 'clone' in request.POST: clone_data = {} clone_data['name'] = request.POST.get('name', '') - disk_sum = sum([disk['size']>>30 for disk in disks]) + disk_sum = sum([disk['size'] >> 30 for disk in disks]) quota_msg = check_user_quota(1, vcpu, memory, disk_sum) check_instance = Instance.objects.filter(name=clone_data['name']) - + for post in request.POST: clone_data[post] = request.POST.get(post, '').strip() - + if clone_instance_auto_name and not clone_data['name']: auto_vname = clone_free_names[0] clone_data['name'] = auto_vname @@ -776,7 +678,7 @@ def instance(request, compute_id, vname): disk_dev = "disk-{}".format(disk['dev']) disk_name = get_clone_disk_name(disk, vname, auto_vname) clone_data[disk_dev] = disk_name - + if not request.user.is_superuser and quota_msg: msg = _("User %s quota reached, cannot create '%s'!" % (quota_msg, clone_data['name'])) error_messages.append(msg) @@ -786,14 +688,16 @@ def instance(request, compute_id, vname): elif not re.match(r'^[a-zA-Z0-9-]+$', clone_data['name']): msg = _("Instance name '%s' contains invalid characters!" % clone_data['name']) error_messages.append(msg) - elif not re.match(r'^([0-9A-F]{2})(\:?[0-9A-F]{2}){5}$', clone_data['clone-net-mac-0'], re.IGNORECASE): + elif not re.match(r'^([0-9A-F]{2})(\:?[0-9A-F]{2}){5}$', clone_data['clone-net-mac-0'], + re.IGNORECASE): msg = _("Instance mac '%s' invalid format!" % clone_data['clone-net-mac-0']) error_messages.append(msg) else: new_uuid = conn.clone_instance(clone_data) new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid) new_instance.save() - userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True) + userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, + is_delete=True) userinstance.save() msg = _("Clone of '%s'" % instance.name) @@ -801,18 +705,19 @@ def instance(request, compute_id, vname): if settings.CLONE_INSTANCE_AUTO_MIGRATE: new_compute = Compute.objects.order_by('?').first() migrate_instance(new_compute, new_instance, xml_del=True, offline=True) - return HttpResponseRedirect(reverse('instance', args=[new_instance.compute.id, new_instance.name])) + return HttpResponseRedirect( + reverse('instance', args=[new_instance.compute.id, new_instance.name])) if 'change_options' in request.POST: instance.is_template = request.POST.get('is_template', False) instance.save() - + options = {} for post in request.POST: if post in ['title', 'description']: options[post] = request.POST.get(post, '') conn.set_options(options) - + msg = _("Edit options") addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#options') @@ -851,6 +756,154 @@ def inst_status(request, compute_id, vname): return response +def get_host_instances(request,comp): + + def refresh_instance_database(comp, inst_name, info): + def get_userinstances_info(instance): + info = {} + uis = UserInstance.objects.filter(instance=instance) + info['count'] = uis.count() + if info['count'] > 0: + info['first_user'] = uis[0] + else: + info['first_user'] = None + return info + + instances = Instance.objects.filter(name=inst_name) + if instances.count() > 1: + for i in instances: + user_instances_count = UserInstance.objects.filter(instance=i).count() + if user_instances_count == 0: + addlogmsg(request.user.username, i.name, _("Deleting due to multiple records.")) + i.delete() + + try: + inst_on_db = Instance.objects.get(compute_id=comp["id"], name=inst_name) + if inst_on_db.uuid != info['uuid']: + inst_on_db.save() + + all_host_vms[comp["id"], + comp["name"], + comp["status"], + comp["cpu"], + comp["mem_size"], + comp["mem_perc"]][inst_name]['is_template'] = inst_on_db.is_template + all_host_vms[comp["id"], + comp["name"], + comp["status"], + comp["cpu"], + comp["mem_size"], + comp["mem_perc"]][inst_name]['userinstances'] = get_userinstances_info(inst_on_db) + except Instance.DoesNotExist: + inst_on_db = Instance(compute_id=comp["id"], name=inst_name, uuid=info['uuid']) + inst_on_db.save() + + all_host_vms = {} + status = connection_manager.host_is_up(comp.type, comp.hostname) + + if status: + + conn = wvmHostDetails(comp, comp.login, comp.password, comp.type) + comp_node_info = conn.get_node_info() + comp_mem = conn.get_memory_usage() + comp_instances = conn.get_host_instances(True) + + if comp_instances: + comp_info = { + "id": comp.id, + "name": comp.name, + "status": status, + "cpu": comp_node_info[3], + "mem_size": comp_node_info[2], + "mem_perc": comp_mem['percent'] + } + all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"], + comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances + for vm, info in comp_instances.items(): + refresh_instance_database(comp_info, vm, info) + + conn.close() + + return all_host_vms + +def get_user_instances(request): + all_user_vms = {} + user_instances = UserInstance.objects.filter(user_id=request.user.id) + for usr_inst in user_instances: + if connection_manager.host_is_up(usr_inst.instance.compute.type, + usr_inst.instance.compute.hostname): + conn = wvmHostDetails(usr_inst.instance.compute, + usr_inst.instance.compute.login, + usr_inst.instance.compute.password, + usr_inst.instance.compute.type) + all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name) + all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id}) + return all_user_vms + + +def instances_actions(request): + name = request.POST.get('name', '') + compute_id = request.POST.get('compute_id', '') + instance = Instance.objects.get(compute_id=compute_id, name=name) + + conn = wvmInstances(instance.compute.hostname, + instance.compute.login, + instance.compute.password, + instance.compute.type) + if 'poweron' in request.POST: + msg = _("Power On") + addlogmsg(request.user.username, instance.name, msg) + conn.start(name) + return HttpResponseRedirect(request.get_full_path()) + + if 'poweroff' in request.POST: + msg = _("Power Off") + addlogmsg(request.user.username, instance.name, msg) + conn.shutdown(name) + return HttpResponseRedirect(request.get_full_path()) + + if 'powercycle' in request.POST: + msg = _("Power Cycle") + conn.force_shutdown(name) + conn.start(name) + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path()) + + if 'getvvfile' in request.POST: + msg = _("Send console.vv file") + addlogmsg(request.user.username, instance.name, msg) + response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None, + charset='utf-8') + response.writelines('[virt-viewer]\n') + response.writelines('type=' + conn.graphics_type(name) + '\n') + response.writelines('host=' + conn.graphics_listen(name) + '\n') + response.writelines('port=' + conn.graphics_port(name) + '\n') + response.writelines('title=' + conn.domain_name(name) + '\n') + response.writelines('password=' + conn.graphics_passwd(name) + '\n') + response.writelines('enable-usbredir=1\n') + response.writelines('disable-effects=all\n') + response.writelines('secure-attention=ctrl+alt+ins\n') + response.writelines('release-cursor=ctrl+alt\n') + response.writelines('fullscreen=1\n') + response.writelines('delete-this-file=1\n') + response['Content-Disposition'] = 'attachment; filename="console.vv"' + return response + + if request.user.is_superuser: + + if 'suspend' in request.POST: + msg = _("Suspend") + addlogmsg(request.user.username, instance.name, msg) + conn.suspend(name) + return HttpResponseRedirect(request.get_full_path()) + + if 'resume' in request.POST: + msg = _("Resume") + addlogmsg(request.user.username, instance.name, msg) + conn.resume(name) + return HttpResponseRedirect(request.get_full_path()) + + @login_required def inst_graph(request, compute_id, vname): """ diff --git a/interfaces/templates/interface.html b/interfaces/templates/interface.html index 014a44e..5c5c2b5 100644 --- a/interfaces/templates/interface.html +++ b/interfaces/templates/interface.html @@ -10,6 +10,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/interfaces/templates/interfaces.html b/interfaces/templates/interfaces.html index 58d4978..8305b88 100644 --- a/interfaces/templates/interfaces.html +++ b/interfaces/templates/interfaces.html @@ -12,6 +12,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/networks/templates/network.html b/networks/templates/network.html index e3b0275..6fa0c2e 100644 --- a/networks/templates/network.html +++ b/networks/templates/network.html @@ -10,6 +10,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/networks/templates/networks.html b/networks/templates/networks.html index 2acc1f3..b57f7c6 100644 --- a/networks/templates/networks.html +++ b/networks/templates/networks.html @@ -11,6 +11,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html index 9f97831..377190d 100644 --- a/nwfilters/templates/nwfilter.html +++ b/nwfilters/templates/nwfilter.html @@ -12,6 +12,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/nwfilters/templates/nwfilters.html b/nwfilters/templates/nwfilters.html index e237380..dba8377 100644 --- a/nwfilters/templates/nwfilters.html +++ b/nwfilters/templates/nwfilters.html @@ -12,6 +12,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/secrets/templates/secrets.html b/secrets/templates/secrets.html index 07b104a..7f781ce 100644 --- a/secrets/templates/secrets.html +++ b/secrets/templates/secrets.html @@ -15,6 +15,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/storages/templates/storage.html b/storages/templates/storage.html index 4d3c706..17ed99c 100644 --- a/storages/templates/storage.html +++ b/storages/templates/storage.html @@ -16,6 +16,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/storages/templates/storages.html b/storages/templates/storages.html index 026fe13..7529842 100644 --- a/storages/templates/storages.html +++ b/storages/templates/storages.html @@ -11,6 +11,9 @@
  • {% trans "Overview" %}
  • +
  • + {% trans "Instances" %} +
  • {% trans "Storages" %}
  • diff --git a/templates/navbar.html b/templates/navbar.html index 6b69be4..1a1263b 100644 --- a/templates/navbar.html +++ b/templates/navbar.html @@ -15,7 +15,7 @@ -
    +

    {{ hostname }}

    {% for arch, hpv in hypervisor.items %} diff --git a/computes/views.py b/computes/views.py index 18ad680..db3887c 100644 --- a/computes/views.py +++ b/computes/views.py @@ -156,7 +156,7 @@ def overview(request, compute_id): 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() - emulator = conn.emulator() + emulator = conn.get_emulator(host_arch) conn.close() except libvirtError as lib_err: error_messages.append(lib_err) diff --git a/instances/templates/instances.html b/instances/templates/instances.html index 987ad02..28a6d34 100644 --- a/instances/templates/instances.html +++ b/instances/templates/instances.html @@ -10,7 +10,9 @@

    {% if request.user.is_superuser %} - {% include 'create_inst_block.html' %} + + + {% endif %} {% if all_host_vms or all_user_vms %} +
    +
    + + {% if request.user.is_superuser %}

    {% trans "Autostart your instance when host server is power on" %}

    @@ -884,9 +869,6 @@
    {% endfor %} - - -
    @@ -950,7 +932,7 @@
    {% ifequal disk.format 'qcow2' %} - +
    @@ -1561,7 +1543,7 @@ } }); } - if (~$.inArray(hash, ['#media', '#network', '#clone', '#autostart', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) { + if (~$.inArray(hash, ['#media', "#disks", '#network', '#clone', '#autostart', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) { var btnsect = $('#navbtn>li>a'); $(btnsect).each(function () { if ($(this).attr('href') === '#settings') { diff --git a/instances/views.py b/instances/views.py index 375d63c..4f90d85 100644 --- a/instances/views.py +++ b/instances/views.py @@ -342,7 +342,7 @@ def instance(request, compute_id, vname): if request.POST.get('delete_disk', ''): for snap in snapshots: conn.snapshot_delete(snap['name']) - conn.delete_disk() + conn.delete_all_disks() conn.delete() instance = Instance.objects.get(compute_id=compute_id, name=vname) @@ -457,7 +457,34 @@ def instance(request, compute_id, vname): conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus) msg = _('Attach new disk') addlogmsg(request.user.username, instance.name, msg) - return HttpResponseRedirect(request.get_full_path() + '#resize') + return HttpResponseRedirect(request.get_full_path() + '#disks') + + if 'delvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): + connDelete = wvmCreate(compute.hostname, + compute.login, + compute.password, + compute.type) + dev = request.POST.get('dev', '') + path = request.POST.get('path', '') + + conn.detach_disk(dev, path) + connDelete.delete_volume(path) + + msg = _('Delete disk') + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#disks') + + if 'detachvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): + connDelete = wvmCreate(compute.hostname, + compute.login, + compute.password, + compute.type) + dev = request.POST.get('dev', '') + path = request.POST.get('path', '') + conn.detach_disk(dev, path) + msg = _('Detach disk') + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#disks') if 'umount_iso' in request.POST: image = request.POST.get('path', '') @@ -1154,7 +1181,7 @@ def delete_instance(instance, delete_disk=False): print("Deleting snapshot {}".format(snap['name'])) conn.snapshot_delete(snap['name']) print("Deleting disks") - conn.delete_disk() + conn.delete_all_disks() conn.delete() instance.delete() diff --git a/vrtManager/instance.py b/vrtManager/instance.py index b5f3f16..6c05c3c 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -259,6 +259,7 @@ class wvmInstance(wvmConnect): if device == 'disk': try: dev = disk.xpath('target/@dev')[0] + bus = disk.xpath('target/@bus')[0] src_fl = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0] try: disk_format = disk.xpath('driver/@type')[0] @@ -267,7 +268,9 @@ class wvmInstance(wvmConnect): try: vol = self.get_volume_by_path(src_fl) volume = vol.name() + disk_size = vol.info()[1] + used_size = vol.info()[2] stg = vol.storagePoolLookupByVolume() storage = stg.name() except libvirtError: @@ -276,8 +279,8 @@ class wvmInstance(wvmConnect): pass finally: result.append( - {'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl, - 'format': disk_format, 'size': disk_size}) + {'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_fl, + 'format': disk_format, 'size': disk_size, 'used': used_size}) return result return util.get_xml_path(self._XMLDesc(0), func=disks) @@ -376,6 +379,25 @@ class wvmInstance(wvmConnect): xmldom = ElementTree.tostring(tree) self._defineXML(xmldom) + def detach_disk(self, dev, image): + tree = ElementTree.fromstring(self._XMLDesc(0)) + + for disk in tree.findall("./devices/disk[@device='disk']"): + source = disk.find("source") + target = disk.find("target") + if source.get("file") == image and target.get("dev") == dev: + devices = tree.find('devices') + devices.remove(disk) + + if self.get_status() == 1: + xml_disk = ElementTree.tostring(disk) + yyy = self.instance.detachDevice(xml_disk) + xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE) + if self.get_status() == 5: + xmldom = ElementTree.tostring(tree) + break + self._defineXML(xmldom) + def cpu_usage(self): cpu_usage = {} if self.get_status() == 1: @@ -628,7 +650,7 @@ class wvmInstance(wvmConnect): iso.append(img) return iso - def delete_disk(self): + def delete_all_disks(self): disks = self.get_disk_device() for disk in disks: vol = self.get_volume_by_path(disk.get('path')) From 03ffa3a295492c07476cb2307a54fd88e9fe9f38 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 12:04:05 +0300 Subject: [PATCH 31/41] move add new disk to instance disk tab. add existing disk option to attach disk. Small typo fixes --- instances/templates/add_instance_volume.html | 213 ++++++++++++------- instances/templates/instance.html | 106 +++++---- instances/views.py | 35 ++- storages/views.py | 9 +- vrtManager/instance.py | 6 +- 5 files changed, 252 insertions(+), 117 deletions(-) diff --git a/instances/templates/add_instance_volume.html b/instances/templates/add_instance_volume.html index fb3ce22..3c94225 100644 --- a/instances/templates/add_instance_volume.html +++ b/instances/templates/add_instance_volume.html @@ -12,79 +12,150 @@
    -
    {% csrf_token %} - - -
    + +
    diff --git a/instances/templates/instance.html b/instances/templates/instance.html index ecc410b..2cae09d 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -631,8 +631,8 @@

    - {% trans "Instance Volumes" %} - {% include 'add_instance_volume.html' %} + {% trans "Instance Volumes" %} + {% include 'add_instance_volume.html' %}

    @@ -648,38 +648,39 @@ {% trans "Action" %} - {% for disk in disks %} - - {{ disk.dev }}
    {{ disk.target }} - {{ disk.format }} - {{ disk.used | filesizeformat}} - {{ disk.size | filesizeformat }} - {{ disk.bus }} - {{ disk.storage }} - {{ disk.path }} - -
    {% csrf_token %} - - - {% ifequal status 5 %} - - - {% else %} - - - {% endifequal %} -
    - - - {% endfor %} + {% for disk in disks %} + + + {{ disk.dev }}
    {{ disk.target }} + {{ disk.format }} + {{ disk.used | filesizeformat}} + {{ disk.size | filesizeformat }} + {{ disk.bus }} + {{ disk.storage }} + {{ disk.path }} + +
    {% csrf_token %} + + + {% ifequal status 5 %} + + + {% else %} + + + {% endifequal %} +
    + + + {% endfor %}
    @@ -1227,6 +1228,39 @@ {% endblock %} {% block script %} + + diff --git a/instances/views.py b/instances/views.py index 4f90d85..e98ca86 100644 --- a/instances/views.py +++ b/instances/views.py @@ -20,6 +20,7 @@ from vrtManager.hostdetails import wvmHostDetails from vrtManager.instance import wvmInstance, wvmInstances from vrtManager.connection import connection_manager from vrtManager.create import wvmCreate +from vrtManager.storage import wvmStorage from vrtManager.util import randomPasswd from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE from logs.views import addlogmsg @@ -171,7 +172,7 @@ def instance(request, compute_id, vname): usr_inst.instance.name) cpu += int(conn.get_vcpu()) memory += int(conn.get_memory()) - for disk in conn.get_disk_device(): + for disk in conn.get_disk_devices(): if disk['size']: disk_size += int(disk['size']) >> 30 @@ -255,8 +256,8 @@ def instance(request, compute_id, vname): cur_memory = conn.get_cur_memory() title = conn.get_title() description = conn.get_description() - disks = conn.get_disk_device() - media = conn.get_media_device() + disks = conn.get_disk_devices() + media = conn.get_media_devices() if len(media) != 0: media_iso = sorted(conn.get_iso_media()) else: @@ -439,7 +440,7 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#resize') - if 'addvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): + if 'addnewvol' in request.POST and (request.user.is_superuser or userinstance.is_change): connCreate = wvmCreate(compute.hostname, compute.login, compute.password, @@ -459,6 +460,26 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#disks') + if 'addexistingvol' in request.POST and (request.user.is_superuser or userinstance.is_change): + storage = request.POST.get('selected_storage', '') + name = request.POST.get('vols', '') + bus = request.POST.get('bus', default_bus) + cache = request.POST.get('cache', default_cache) + + connCreate = wvmStorage(compute.hostname, + compute.login, + compute.password, + compute.type, + storage) + + format = connCreate.get_volume_type(name) + path = connCreate.get_target_path() + target = get_new_disk_dev(disks, bus) + source = path + "/" + name; + + conn.attach_disk(source, target, subdriver=format, cache=cache, targetbus=bus) + return HttpResponseRedirect(request.get_full_path() + '#disks') + if 'delvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): connDelete = wvmCreate(compute.hostname, compute.login, @@ -1056,7 +1077,7 @@ def _get_dhcp_mac_address(vname): @login_required def guess_mac_address(request, vname): - data = { 'vname': vname } + data = {'vname': vname} mac = _get_dhcp_mac_address(vname) if not mac: mac = _get_random_mac_address() @@ -1100,7 +1121,7 @@ def guess_clone_name(request): @login_required def check_instance(request, vname): check_instance = Instance.objects.filter(name=vname) - data = { 'vname': vname, 'exists': False } + data = {'vname': vname, 'exists': False} if check_instance: data['exists'] = True return HttpResponse(json.dumps(data)) @@ -1119,6 +1140,7 @@ def get_clone_disk_name(disk, prefix, clone_name=''): image = "{}-clone".format(disk['image']) return image + def _get_clone_disks(disks, vname=''): clone_disks = [] for disk in disks: @@ -1134,6 +1156,7 @@ def _get_clone_disks(disks, vname=''): clone_disks.append(new_disk) return clone_disks + def sshkeys(request, vname): """ :param request: diff --git a/storages/views.py b/storages/views.py index d2214aa..4893a27 100644 --- a/storages/views.py +++ b/storages/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render, get_object_or_404 -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponse from django.utils.translation import ugettext_lazy as _ from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required @@ -8,6 +8,7 @@ from storages.forms import AddStgPool, AddImage, CloneImage from vrtManager.storage import wvmStorage, wvmStorages from libvirt import libvirtError from django.contrib import messages +import json @login_required def storages(request, compute_id): @@ -204,6 +205,12 @@ def storage(request, compute_id, pool): else: for msg_err in form.errors.values(): error_messages.append(msg_err.as_text()) + if request.method == 'GET': + if 'get_volumes' in request.GET: + conn.close() + return HttpResponse(json.dumps(sorted(volumes))) + conn.close() return render(request, 'storage.html', locals()) + diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 6c05c3c..eb38096 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -244,7 +244,7 @@ class wvmInstance(wvmConnect): return util.get_xml_path(self._XMLDesc(0), func=networks) - def get_disk_device(self): + def get_disk_devices(self): def disks(doc): result = [] dev = None @@ -285,7 +285,7 @@ class wvmInstance(wvmConnect): return util.get_xml_path(self._XMLDesc(0), func=disks) - def get_media_device(self): + def get_media_devices(self): def disks(doc): result = [] dev = None @@ -651,7 +651,7 @@ class wvmInstance(wvmConnect): return iso def delete_all_disks(self): - disks = self.get_disk_device() + disks = self.get_disk_devices() for disk in disks: vol = self.get_volume_by_path(disk.get('path')) vol.delete(0) From 2b04a891003921e43cb281f92de4c9ced07fbd92 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 14:09:29 +0300 Subject: [PATCH 32/41] make some details showing style popover --- instances/templates/instance.html | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 2cae09d..8747807 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -639,10 +639,8 @@ - - @@ -650,15 +648,26 @@ {% for disk in disks %} - - - + - - + + + {% endfor %} + +
    {% trans "Device" %}{% trans "Format" %} {% trans "Used" %} {% trans "Capacity" %}{% trans "Bus" %} {% trans "Storage" %} {% trans "Source" %} {% trans "Action" %}
    {{ disk.dev }}
    {{ disk.target }}
    {{ disk.format }} + + {{ disk.dev }} +
    + {{ disk.target }} +
    {{ disk.used | filesizeformat}} {{ disk.size | filesizeformat }}{{ disk.bus }} {{ disk.storage }} {{ disk.path }} +
    {% csrf_token %} @@ -1623,5 +1632,12 @@ $("#logs_table > tbody").html(logs); }); } + + {% endblock %} From 9d07c2d7b60f738f634db8e7c60329f90a6988a8 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 16:14:14 +0300 Subject: [PATCH 33/41] new instance moved to tabs instead of collapsed divs by @honza801 --- create/templates/create_instance.html | 949 +++++++++++++------------- 1 file changed, 485 insertions(+), 464 deletions(-) diff --git a/create/templates/create_instance.html b/create/templates/create_instance.html index 897cccd..33eeca8 100644 --- a/create/templates/create_instance.html +++ b/create/templates/create_instance.html @@ -6,497 +6,518 @@ {% endblock %} {% block content %} - -
    -
    - {% include 'create_flav_block.html' %} -

    {% trans "New instance on" %} {{ compute.name }}

    -
    -
    - - {% include 'errors_block.html' %} - {% include 'pleasewaitdialog.html' %} + +
    +
    +

    {% trans "New instance on" %} {{ compute.name }}

    +
    +
    + + {% include 'errors_block.html' %} + {% include 'pleasewaitdialog.html' %} -
    -
    -

    -

    + +
    +
    + {% if not flavors %} +
    +
    + + {% trans "Warning:" %} {% trans "Hypervisor doesn't have any Flavors" %}
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    -
      - -
    - - -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    -
    - -
    -
      - -
    - - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    - {% if storages %} - - {% else %} - - {% endif %} - -
    -
    -
    -
    -
    {% csrf_token %} -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    - - {% if storages %} - - {% else %} - - {% endif %} -
    -
    -
    -
    -
    -
    {% csrf_token %} -
    - - -
    - -
    -
    -
    -
    -
    - -
    - {% if not flavors %} -
    -
    - - {% trans "Warning:" %} {% trans "Hypervisor doesn't have any Flavors" %} -
    -
    - {% else %} -
    - -
    - - - - - - - - - - - - - {% for flavor in flavors %} + {% else %} + {% include 'create_flav_block.html' %} + +
    +
    #{% trans "Name" %}{% trans "VCPU's" %}{% trans "RAM" %}{% trans "HDD" %}{% trans "Action" %}
    + - - - - - - + + + + + + + + + {% for flavor in flavors %} + + + + + + + - - - {% endfor %} - -
    {{ forloop.counter }}{{ flavor.label }}{{ flavor.vcpu }}{{ flavor.memory }} {% trans "MB" %}{{ flavor.disk }} {% trans "GB" %} - #{% trans "Name" %}{% trans "VCPU's" %}{% trans "RAM" %}{% trans "HDD" %}{% trans "Action" %}
    {{ forloop.counter }}{{ flavor.label }}{{ flavor.vcpu }}{{ flavor.memory }} {% trans "MB" %}{{ flavor.disk }} {% trans "GB" %} + -
    {% csrf_token %} - - -
    -
    + + + +
    +
    {% csrf_token %} + + +
    +
    +
    + {% endif %} +
    + + +
    +
    +
    {% csrf_token %} +
    + +
    +
    - {% endif %} +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    +
      + +
    + + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
      + +
    + + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + {% if storages %} + + {% else %} + + {% endif %} +
    +
    +
    + +
    +
    +
    {% csrf_token %} +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + + {% if storages %} + + {% else %} + + {% endif %} +
    +
    +
    +
    + +
    +
    +
    {% csrf_token %} +
    + + +
    + +
    +
    +
    +
    + + + + {% endblock %} {% block script %} From b5f38afbcae965e879e05ce19c4c9cf583b22177 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 16:19:30 +0300 Subject: [PATCH 34/41] clone instance: create db record first, then run clone process. delete db record if exception while cloning --- instances/views.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/instances/views.py b/instances/views.py index e98ca86..aff0e78 100644 --- a/instances/views.py +++ b/instances/views.py @@ -741,11 +741,16 @@ def instance(request, compute_id, vname): msg = _("Instance mac '%s' invalid format!" % clone_data['clone-net-mac-0']) error_messages.append(msg) else: - new_uuid = conn.clone_instance(clone_data) - new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid) + new_instance = Instance(compute_id=compute_id, name=clone_data['name']) new_instance.save() - userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, - is_delete=True) + try: + new_uuid = conn.clone_instance(clone_data) + new_instance.uuid = new_uuid + new_instance.save() + except Exception as e: + new_instance.delete() + raise e + userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True) userinstance.save() msg = _("Clone of '%s'" % instance.name) From b3b9596a125d65c73442b7f0453e2e654c0f64bb Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 16:42:00 +0300 Subject: [PATCH 35/41] secure instance snapshot, media, options. check userinstance.is_change and instance.is_template correctly. secure mount_iso, snapshots for templates, not userinstance.is_change by @honza801 --- instances/templates/instance.html | 22 ++++++++++++++-------- instances/views.py | 12 ++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 8747807..6107285 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -72,12 +72,14 @@ {% trans "Resize" %} -
  • - - - {% trans "Snapshots" %} - -
  • + {% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %} +
  • + + + {% trans "Snapshots" %} + +
  • + {% endif %}
  • @@ -609,7 +611,7 @@
    - {% if media_iso %} + {% if media_iso and request.user.is_superuser or request.user.is_staff or not userinstance.is_template %} {% else %} @@ -621,7 +623,11 @@
    - + {% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %} + + {% else %} + + {% endif %}
    {% endif %} diff --git a/instances/views.py b/instances/views.py index aff0e78..e81c35d 100644 --- a/instances/views.py +++ b/instances/views.py @@ -507,7 +507,7 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#disks') - if 'umount_iso' in request.POST: + if 'umount_iso' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template): image = request.POST.get('path', '') dev = request.POST.get('umount_iso', '') conn.umount_iso(dev, image) @@ -515,7 +515,7 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#media') - if 'mount_iso' in request.POST: + if 'mount_iso' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template): image = request.POST.get('media', '') dev = request.POST.get('mount_iso', '') conn.mount_iso(dev, image) @@ -523,21 +523,21 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#media') - if 'snapshot' in request.POST: + if 'snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template): name = request.POST.get('name', '') conn.create_snapshot(name) msg = _("New snapshot") addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#managesnapshot') - if 'delete_snapshot' in request.POST: + if 'delete_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template): snap_name = request.POST.get('name', '') conn.snapshot_delete(snap_name) msg = _("Delete snapshot") addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#managesnapshot') - if 'revert_snapshot' in request.POST: + if 'revert_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template): snap_name = request.POST.get('name', '') conn.snapshot_revert(snap_name) msg = _("Successful revert snapshot: ") @@ -761,7 +761,7 @@ def instance(request, compute_id, vname): return HttpResponseRedirect( reverse('instance', args=[new_instance.compute.id, new_instance.name])) - if 'change_options' in request.POST: + if 'change_options' in request.POST and (request.user.is_superuser or request.user.is_staff or userinstance.is_change): instance.is_template = request.POST.get('is_template', False) instance.save() From e80bbfd85b0e7a55cde870d5c12ded5d952b1899 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 16:45:23 +0300 Subject: [PATCH 36/41] console/templates/console-base.html fix html tag console/templates/console-spice-lite.html add console log message by @honza801 --- console/templates/console-base.html | 2 +- console/templates/console-spice-lite.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/console/templates/console-base.html b/console/templates/console-base.html index c7ea6d4..1446850 100644 --- a/console/templates/console-base.html +++ b/console/templates/console-base.html @@ -89,7 +89,7 @@ -/nav> +
    {% block content %}{% endblock %}
    diff --git a/console/templates/console-spice-lite.html b/console/templates/console-spice-lite.html index 4d4c4e3..8f89678 100644 --- a/console/templates/console-spice-lite.html +++ b/console/templates/console-spice-lite.html @@ -108,7 +108,7 @@ function connect() { var host, port, password, scheme = "ws://", uri; - + console.log('>> connect'); // By default, use the host and port of server that served this file //host = spice_query_var('host', window.location.hostname); host = '{{ ws_host| safe }}'; @@ -170,7 +170,7 @@ disconnect(); } - + console.log('<< connect') } function disconnect() From 1b913fd4d627cc5b1b1bb0567f18ef22855d4948 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 16:56:05 +0300 Subject: [PATCH 37/41] instance poweron checks is_template attribute. templates should not be e started. by @honza801 --- instances/views.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/instances/views.py b/instances/views.py index e81c35d..86a5608 100644 --- a/instances/views.py +++ b/instances/views.py @@ -313,10 +313,14 @@ def instance(request, compute_id, vname): if request.method == 'POST': if 'poweron' in request.POST: - conn.start() - msg = _("Power On") - addlogmsg(request.user.username, instance.name, msg) - return HttpResponseRedirect(request.get_full_path() + '#poweron') + if instance.is_template: + msg = _("Templates cannot be started.") + error_messages.append(msg) + else: + conn.start() + msg = _("Power On") + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#poweron') if 'powercycle' in request.POST: conn.force_shutdown() @@ -904,10 +908,14 @@ def instances_actions(request): instance.compute.password, instance.compute.type) if 'poweron' in request.POST: - msg = _("Power On") - addlogmsg(request.user.username, instance.name, msg) - conn.start(name) - return HttpResponseRedirect(request.get_full_path()) + if instance.is_template: + msg = _("Templates cannot be started.") + messages.error(request, msg) + else: + msg = _("Power On") + addlogmsg(request.user.username, instance.name, msg) + conn.start(name) + return HttpResponseRedirect(request.get_full_path()) if 'poweroff' in request.POST: msg = _("Power Off") From 82c87f82bc694f9d0c397d6c79210f1076914994 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 24 Oct 2018 17:15:10 +0300 Subject: [PATCH 38/41] add/modify alert messages while detaching and deleting volumes. --- instances/templates/instance.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 6107285..5303c37 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -678,17 +678,17 @@ {% ifequal status 5 %} - - {% else %} - - {% endifequal %} @@ -702,7 +702,6 @@
    - {% if request.user.is_superuser %}

    {% trans "Autostart your instance when host server is power on" %}

    From 1196fb38c9ce3ce4d31ea8cbb63a7368fda0333d Mon Sep 17 00:00:00 2001 From: catborise Date: Thu, 25 Oct 2018 09:55:57 +0300 Subject: [PATCH 39/41] add log message to attaching disk --- instances/views.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/instances/views.py b/instances/views.py index 86a5608..b955b6a 100644 --- a/instances/views.py +++ b/instances/views.py @@ -471,10 +471,10 @@ def instance(request, compute_id, vname): cache = request.POST.get('cache', default_cache) connCreate = wvmStorage(compute.hostname, - compute.login, - compute.password, - compute.type, - storage) + compute.login, + compute.password, + compute.type, + storage) format = connCreate.get_volume_type(name) path = connCreate.get_target_path() @@ -482,6 +482,8 @@ def instance(request, compute_id, vname): source = path + "/" + name; conn.attach_disk(source, target, subdriver=format, cache=cache, targetbus=bus) + msg = _('Attach Existing disk') + addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#disks') if 'delvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): From 07984a2a9ab5c7bbf62f4c56f81dc6249d255a6b Mon Sep 17 00:00:00 2001 From: catborise Date: Thu, 25 Oct 2018 09:57:41 +0300 Subject: [PATCH 40/41] attach volume dropdown menu rearranged to show selected pool --- instances/templates/add_instance_volume.html | 4 ++-- instances/templates/instance.html | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/instances/templates/add_instance_volume.html b/instances/templates/add_instance_volume.html index 3c94225..8d0ac97 100644 --- a/instances/templates/add_instance_volume.html +++ b/instances/templates/add_instance_volume.html @@ -103,14 +103,14 @@
    diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 5303c37..dba2547 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -1253,8 +1253,13 @@ select.removeChild(select.options[0]); } - var input_sto = document.getElementById('selected_storage'); - input_sto.value = pool; + var sto_drop = document.getElementById('select_storage'); + sto_drop.value = pool; + sto_drop.innerHTML = pool + ""; + + var sto_input = document.getElementById('selected_storage'); + sto_input.value = pool; + sto_input.innerHTML = pool; $.getJSON(vol_url, function(data) { if (data.length > 0) { From 2ca2add444ce534c9e045b051c61091edfa104a9 Mon Sep 17 00:00:00 2001 From: catborise Date: Fri, 26 Oct 2018 17:30:52 +0300 Subject: [PATCH 41/41] fix typo. remove unused variable. add form-control to volume dropdown. --- instances/templates/add_instance_volume.html | 5 +---- instances/templates/instance.html | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/instances/templates/add_instance_volume.html b/instances/templates/add_instance_volume.html index 8d0ac97..2f2ec3d 100644 --- a/instances/templates/add_instance_volume.html +++ b/instances/templates/add_instance_volume.html @@ -12,9 +12,6 @@ - - -