-
+
+
+ eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})
-
-
+
-
-
- {% for c_nets in compute_networks %}
- Network {{ c_nets }}
- {% endfor %}
- {% for c_iface in compute_interfaces %}
- Interface {{ c_iface }}
- {% endfor %}
-
+
+
{% endfor %}
- {% ifequal status 5 %}
-
{% trans "Change" %}
- {% else %}
-
{% trans "Change" %}
- {% endifequal %}
+
+
+
+
+
{% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
@@ -1370,12 +1394,6 @@
});
});
-
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/networks/templates/network.html b/networks/templates/network.html
index 7d922b1..085b35b 100644
--- a/networks/templates/network.html
+++ b/networks/templates/network.html
@@ -10,6 +10,9 @@
{% trans "Overview" %}
+
+ {% trans "Instances" %}
+
{% trans "Storages" %}
@@ -19,6 +22,9 @@
{% trans "Interfaces" %}
+
+ {% trans "NWFilters" %}
+
{% trans "Secrets" %}
@@ -184,4 +190,4 @@
});
});
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/networks/templates/networks.html b/networks/templates/networks.html
index 36322e2..5e843ae 100644
--- a/networks/templates/networks.html
+++ b/networks/templates/networks.html
@@ -11,6 +11,9 @@
{% trans "Overview" %}
+
+ {% trans "Instances" %}
+
{% trans "Storages" %}
@@ -20,6 +23,9 @@
{% trans "Interfaces" %}
+
+ {% trans "NWFilters" %}
+
{% trans "Secrets" %}
@@ -77,4 +83,4 @@
}).change();
});
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/nwfilters/__init__.py b/nwfilters/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nwfilters/admin.py b/nwfilters/admin.py
new file mode 100644
index 0000000..13be29d
--- /dev/null
+++ b/nwfilters/admin.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.contrib import admin
+
+# Register your models here.
diff --git a/nwfilters/apps.py b/nwfilters/apps.py
new file mode 100644
index 0000000..11475c5
--- /dev/null
+++ b/nwfilters/apps.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.apps import AppConfig
+
+
+class NwfiltersConfig(AppConfig):
+ name = 'nwfilters'
diff --git a/nwfilters/migrations/__init__.py b/nwfilters/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nwfilters/models.py b/nwfilters/models.py
new file mode 100644
index 0000000..1dfab76
--- /dev/null
+++ b/nwfilters/models.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models
+
+# Create your models here.
diff --git a/nwfilters/templates/create_nwfilter_block.html b/nwfilters/templates/create_nwfilter_block.html
new file mode 100644
index 0000000..01056ab
--- /dev/null
+++ b/nwfilters/templates/create_nwfilter_block.html
@@ -0,0 +1,32 @@
+{% load i18n %}
+{% if request.user.is_superuser %}
+
+
+
+
+
+
+{% endif %}
diff --git a/nwfilters/templates/nwfilter.html b/nwfilters/templates/nwfilter.html
new file mode 100644
index 0000000..9f97831
--- /dev/null
+++ b/nwfilters/templates/nwfilter.html
@@ -0,0 +1,220 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load staticfiles %}
+{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
+{% block content %}
+
+
+
+
+ {% include 'errors_block.html' %}
+ {% include 'messages_block.html' %}
+
+
+
+
{% trans "UUID:" %}
+
{% trans "Name:" %}
+
+
+
+
{{ uuid }}
+
{{ name }}
+
+
+
+
+
{% trans "XML:" %}
+
{% csrf_token %}
+
+
+ {{ xml }}
+
+
+ {% trans "Edit" %}
+
+
+
+
+
{% trans "Filter References:" %}
+
{% csrf_token %}
+
+ {% trans "Filter:" %}
+
+ None
+ {% for nwf in nwfilters_all %}
+ {{ nwf.name }}
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #
+ {% trans "Reference" %}
+ {% trans "Action" %}
+
+
+
+ {% for ref in refs %}
+
+ {{ forloop.counter }}
+ {{ ref }}
+
+ {% csrf_token %}
+
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
{% trans "Rules:" %}
+
+
+
+
+
+
+
+
+
+
+
{% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% trans "Rule" %}
+ {% trans "ActionType" %}
+ {% trans "Direction" %}
+ {% trans "Priority" %}
+ {% trans "Statematch" %}
+ {% trans "Directives" %}
+ {% trans "Action" %}
+
+
+
+ {% for rule in rules %}
+
+ {{ forloop.counter }}
+ {{ rule.action }}
+ {{ rule.direction }}
+ {{ rule.priority }}
+ {{ rule.statematch }}
+ {{ rule.directives }}
+
+ {% csrf_token %}
+
+
+
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+{% endblock %}
diff --git a/nwfilters/templates/nwfilters.html b/nwfilters/templates/nwfilters.html
new file mode 100644
index 0000000..9fd228f
--- /dev/null
+++ b/nwfilters/templates/nwfilters.html
@@ -0,0 +1,171 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load staticfiles %}
+{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
+{% block content %}
+
+
+
+
+{% include 'errors_block.html' %}
+{% include 'messages_block.html' %}
+
+
+
+
+ {% if nwfilters %}
+
+
+
+
+ #
+ {% trans "UUID" %}
+ {% trans "Name" %}
+ {% trans "Action" %}
+
+
+
+ {% for nwfilter in nwfilters %}
+
+ {{ forloop.counter }}
+ {{ nwfilter.uuid }}
+ {{ nwfilter.name }}
+
+
+
+
+
+
+
+
+ {{ nwfilter.xml }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% csrf_token %}
+
+
+
+
+
+
+
+
+
+ {% csrf_token %}
+
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+ {% else %}
+
+
+ ×
+ {% trans "Warning:" %} {% trans "Hypervisor doesn't have any NWFilters" %}
+
+
+ {% endif %}
+
+
+{% endblock %}
+
+{% block script %}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/nwfilters/tests.py b/nwfilters/tests.py
new file mode 100644
index 0000000..5982e6b
--- /dev/null
+++ b/nwfilters/tests.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/nwfilters/views.py b/nwfilters/views.py
new file mode 100644
index 0000000..c0dd8d8
--- /dev/null
+++ b/nwfilters/views.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.shortcuts import render
+from django.http import HttpResponseRedirect
+from django.shortcuts import render, get_object_or_404
+from django.utils.translation import ugettext_lazy as _
+from django.core.urlresolvers import reverse
+from django.contrib.auth.decorators import login_required
+from computes.models import Compute
+from vrtManager import util
+from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
+from vrtManager.instance import wvmInstances, wvmInstance
+from libvirt import libvirtError
+from logs.views import addlogmsg
+
+
+@login_required
+def nwfilters(request, compute_id):
+ """
+ :param request:
+ :return:
+ """
+
+ if not request.user.is_superuser:
+ return HttpResponseRedirect(reverse('index'))
+
+ error_messages = []
+ nwfilters_all = []
+ compute = get_object_or_404(Compute, pk=compute_id)
+
+ try:
+ conn = wvmNWFilters(compute.hostname,
+ compute.login,
+ compute.password,
+ compute.type)
+
+ if request.method == 'POST':
+ if 'create_nwfilter' in request.POST:
+ xml = request.POST.get('nwfilter_xml', '')
+ if xml:
+ try:
+ util.etree.fromstring(xml)
+ name = util.get_xml_path(xml, '/filter/@name')
+ uuid = util.get_xml_path(xml, '/filter/uuid')
+ except util.etree.ParseError:
+ name = None
+
+ for nwf in nwfilters:
+ if name == nwf.name():
+ error_msg = _("A network filter with this name already exists")
+ raise Exception(error_msg)
+ if uuid == nwf.UUIDString():
+ error_msg = _("A network filter with this uuid already exists")
+ raise Exception(error_msg)
+ else:
+ try:
+ msg = _("Creating NWFilter: %s" % name)
+ conn.create_nwfilter(xml)
+ addlogmsg(request.user.username, compute.hostname, msg)
+ except libvirtError as lib_err:
+ error_messages.append(lib_err.message)
+ addlogmsg(request.user.username, compute.hostname, lib_err.message)
+
+ if 'del_nwfilter' in request.POST:
+ name = request.POST.get('nwfiltername','')
+ msg = _("Deleting NWFilter: %s" % name)
+ in_use = False
+ nwfilter = conn.get_nwfilter(name)
+
+ is_conn = wvmInstances(compute.hostname, compute.login, compute.password, compute.type)
+ instances = is_conn.get_instances()
+ for inst in instances:
+ # if in_use: break
+ i_conn = wvmInstance(compute.hostname, compute.login, compute.password, compute.type, inst)
+ dom_filterrefs = i_conn.get_filterrefs()
+
+ if name in dom_filterrefs:
+ in_use = True
+ msg = _("NWFilter is in use by %s. Cannot be deleted." % inst)
+ error_messages.append(msg)
+ addlogmsg(request.user.username, compute.hostname, msg)
+ i_conn.close()
+ break
+
+ is_conn.close()
+ if nwfilter and not in_use:
+ nwfilter.undefine()
+ addlogmsg(request.user.username, compute.hostname, msg)
+
+ if 'cln_nwfilter' in request.POST:
+
+ name = request.POST.get('nwfiltername','')
+ cln_name = request.POST.get('cln_name', name + '-clone')
+
+ conn.clone_nwfilter(name,cln_name)
+ msg = _("Cloning NWFilter %s as %s" % (name, cln_name))
+ addlogmsg(request.user.username, compute.hostname, msg)
+
+ for nwf in conn.get_nwfilters():
+ nwfilters_all.append(conn.get_nwfilter_info(nwf))
+
+ conn.close()
+ except libvirtError as lib_err:
+ error_messages.append(lib_err)
+ addlogmsg(request.user.username, compute.hostname, lib_err)
+ except Exception as err:
+ error_messages.append(err)
+ addlogmsg(request.user.username, compute.hostname, err)
+
+ return render(request, 'nwfilters.html', {'error_messages': error_messages,
+ 'nwfilters': nwfilters_all,
+ 'compute': compute})
+
+
+@login_required
+def nwfilter(request, compute_id, nwfltr):
+
+ error_messages = []
+ nwfilters_all = []
+ compute = get_object_or_404(Compute, pk=compute_id)
+
+ try:
+ nwfilter = wvmNWFilter(compute.hostname,
+ compute.login,
+ compute.password,
+ compute.type,
+ nwfltr)
+ conn = wvmNWFilters(compute.hostname,
+ compute.login,
+ compute.password,
+ compute.type)
+
+ for nwf in conn.get_nwfilters():
+ nwfilters_all.append(conn.get_nwfilter_info(nwf))
+
+ uuid = nwfilter.get_uuid()
+ name = nwfilter.get_name()
+ xml = nwfilter.get_xml()
+ rules = nwfilter.get_rules()
+ refs = nwfilter.get_filter_refs()
+
+ if request.method == 'POST':
+
+ if 'edit_nwfilter' in request.POST:
+ new_xml = request.POST.get('edit_xml', '')
+
+ if new_xml:
+ nwfilter.delete()
+ try:
+ conn.create_nwfilter(new_xml)
+ except libvirtError as lib_err:
+ conn.create_nwfilter(xml)
+ raise libvirtError(lib_err)
+
+ if 'del_nwfilter_rule' in request.POST:
+ action = request.POST.get('action', '')
+ direction = request.POST.get('direction', '')
+ priority = request.POST.get('priority', '')
+
+ new_xml = nwfilter.delete_rule(action, direction, priority)
+ nwfilter.delete()
+ try:
+ conn.create_nwfilter(new_xml)
+ except libvirtError as lib_err:
+ conn.create_nwfilter(xml)
+ raise libvirtError(lib_err)
+
+ if 'del_nwfilter_ref' in request.POST:
+ ref_name = request.POST.get('ref')
+ new_xml = nwfilter.delete_ref(ref_name)
+ nwfilter.delete()
+ try:
+ conn.create_nwfilter(new_xml)
+ except libvirtError as lib_err:
+ conn.create_nwfilter(xml)
+ raise libvirtError(lib_err)
+
+ if 'add_nwfilter_rule' in request.POST:
+ rule_xml = request.POST.get('nwfilterrule_xml', '')
+ if not rule_xml:
+ return HttpResponseRedirect(request.get_full_path())
+ new_xml = nwfilter.add_rule(rule_xml)
+ nwfilter.delete()
+ try:
+ conn.create_nwfilter(new_xml)
+ except libvirtError as lib_err:
+ conn.create_nwfilter(xml)
+ raise libvirtError(lib_err)
+
+ if 'add_nwfilter_ref' in request.POST:
+ ref_name = request.POST.get('nwfilters_select', '')
+ if not ref_name:
+ return HttpResponseRedirect(request.get_full_path())
+ new_xml = nwfilter.add_ref(ref_name)
+ nwfilter.delete()
+ try:
+ conn.create_nwfilter(new_xml)
+ except libvirtError as lib_err:
+ conn.create_nwfilter(xml)
+ raise libvirtError(lib_err)
+
+ return HttpResponseRedirect(request.get_full_path())
+ conn.close()
+ nwfilter.close()
+ except libvirtError as lib_err:
+ error_messages.append(lib_err)
+ except Exception as error_msg:
+ error_messages.append(error_msg)
+
+ return render(request, 'nwfilter.html', locals())
\ No newline at end of file
diff --git a/secrets/templates/secrets.html b/secrets/templates/secrets.html
index 8286bfb..28d8dbd 100644
--- a/secrets/templates/secrets.html
+++ b/secrets/templates/secrets.html
@@ -10,11 +10,14 @@
{% include 'create_secret_block.html' %}
-
+
{% trans "Overview" %}
+
+ {% trans "Instances" %}
+
{% trans "Storages" %}
@@ -24,6 +27,9 @@
{% trans "Interfaces" %}
+
+ {% trans "NWFilters" %}
+
{% trans "Secrets" %}
@@ -119,4 +125,4 @@
{% endblock %}
{% block script %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/static/js/sortable.min.js b/static/js/sortable.min.js
index 8278f50..b88c451 100755
--- a/static/js/sortable.min.js
+++ b/static/js/sortable.min.js
@@ -1,2 +1,2 @@
/*! sortable.js 0.8.0 */
-(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
\ No newline at end of file
+(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
diff --git a/storages/templates/storage.html b/storages/templates/storage.html
index ecc9adc..09137f6 100644
--- a/storages/templates/storage.html
+++ b/storages/templates/storage.html
@@ -16,6 +16,9 @@
{% trans "Overview" %}
+
+ {% trans "Instances" %}
+
{% trans "Storages" %}
@@ -25,6 +28,9 @@
{% trans "Interfaces" %}
+
+ {% trans "NWFilters" %}
+
{% trans "Secrets" %}
@@ -42,7 +48,7 @@
{% trans "Pool type:" %}
{% trans "Pool path:" %}
{% trans "Pool status:" %}
- {% trans "Size:" %} ({{ size|filesizeformat }} / {{ used|filesizeformat }})
+ {% trans "Size:" %} (Used: {{ used|filesizeformat }} / From total: {{ size|filesizeformat }})
{% trans "State:" %}
{% trans "Autostart:" %}
diff --git a/storages/templates/storages.html b/storages/templates/storages.html
index 21bca90..20f147c 100644
--- a/storages/templates/storages.html
+++ b/storages/templates/storages.html
@@ -11,6 +11,9 @@
{% trans "Overview" %}
+
+ {% trans "Instances" %}
+
{% trans "Storages" %}
@@ -20,6 +23,9 @@
{% trans "Interfaces" %}
+
+ {% trans "NWFilters" %}
+
{% trans "Secrets" %}
@@ -64,4 +70,4 @@
{% endfor %}
{% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/storages/views.py b/storages/views.py
index f18cad6..d2214aa 100644
--- a/storages/views.py
+++ b/storages/views.py
@@ -155,7 +155,7 @@ def storage(request, compute_id, pool):
meta_prealloc = True
try:
conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc)
- messages.success("Image file {} is created successfully".format(data['name']+".img"))
+ messages.success(request, _("Image file {} is created successfully".format(data['name']+".img")))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err)
@@ -167,7 +167,7 @@ def storage(request, compute_id, pool):
try:
vol = conn.get_volume(volname)
vol.delete(0)
- messages.success(request,_('Volume: {} is deleted.'.format(volname)))
+ messages.success(request, _('Volume: {} is deleted.'.format(volname)))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
@@ -197,8 +197,7 @@ def storage(request, compute_id, pool):
format = None
try:
conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
- messages.success(request, _("{} image cloned as {} successfully".format(data['image'],
- data['name'] + ".img")))
+ messages.success(request, _("{} image cloned as {} successfully".format(data['image'], data['name'] + ".img")))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err)
diff --git a/vrtManager/connection.py b/vrtManager/connection.py
index 65c2915..1a16f4e 100644
--- a/vrtManager/connection.py
+++ b/vrtManager/connection.py
@@ -389,6 +389,12 @@ class wvmConnect(object):
interface.append(inface)
return interface
+ def get_nwfilters(self):
+ nwfilters = []
+ for nwfilter in self.wvm.listNWFilters():
+ nwfilters.append(nwfilter)
+ return nwfilters
+
def get_cache_modes(self):
"""Get cache available modes"""
return {
@@ -443,7 +449,6 @@ class wvmConnect(object):
def get_video(self):
""" Get available graphics video types """
-
def get_video_list(ctx):
result = []
for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'):
@@ -470,6 +475,9 @@ class wvmConnect(object):
def get_network(self, net):
return self.wvm.networkLookupByName(net)
+ def get_nwfilter(self, name):
+ return self.wvm.nwfilterLookupByName(name)
+
def get_instance(self, name):
return self.wvm.lookupByName(name)
diff --git a/vrtManager/instance.py b/vrtManager/instance.py
index b27baa0..d5bc1b9 100644
--- a/vrtManager/instance.py
+++ b/vrtManager/instance.py
@@ -187,6 +187,19 @@ class wvmInstance(wvmConnect):
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
return title if title else ''
+ def get_filterrefs(self):
+
+ def filterrefs(ctx):
+ result = []
+ for net in ctx.xpath('/domain/devices/interface'):
+ filterref = net.xpath('filterref/@filter')
+ if filterref:
+ result.append(filterref[0])
+ return result
+
+ return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
+
+
def get_description(self):
description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
return description if description else ''
@@ -219,12 +232,13 @@ class wvmInstance(wvmConnect):
mac_host = net.xpath('mac/@address')[0]
network_host = net.xpath('source/@network|source/@bridge|source/@dev')[0]
target_host = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0]
+ filterref_host = '' if not net.xpath('filterref/@filter') else net.xpath('filterref/@filter')[0]
try:
net = self.get_network(network_host)
ip = get_mac_ipaddr(net, mac_host)
except libvirtError as e:
ip = None
- result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip})
+ result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip, 'filterref': filterref_host})
return result
return util.get_xml_path(self._XMLDesc(0), func=networks)
@@ -487,8 +501,7 @@ class wvmInstance(wvmConnect):
return self._defineXML(newxml)
def get_console_socket(self):
- socket = util.get_xml_path(self._XMLDesc(0),
- "/domain/devices/graphics/@socket")
+ socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket")
return socket
def get_console_type(self):
@@ -700,13 +713,11 @@ class wvmInstance(wvmConnect):
source_file = elm.get('file')
if source_file:
clone_dev_path.append(source_file)
- clone_path = os.path.join(os.path.dirname(source_file),
- target_file)
+ clone_path = os.path.join(os.path.dirname(source_file), target_file)
elm.set('file', clone_path)
vol = self.get_volume_by_path(source_file)
- vol_format = util.get_xml_path(vol.XMLDesc(0),
- "/volume/target/format/@type")
+ vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
if vol_format == 'qcow2' and meta_prealloc:
meta_prealloc = True
@@ -729,8 +740,7 @@ class wvmInstance(wvmConnect):
elm.set('name', clone_name)
vol = self.get_volume_by_path(source_name)
- vol_format = util.get_xml_path(vol.XMLDesc(0),
- "/volume/target/format/@type")
+ vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
vol_clone_xml = """
@@ -776,7 +786,7 @@ class wvmInstance(wvmConnect):
bridge_name = net.bridgeName()
return bridge_name
- def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio'):
+ def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio', nwfilter=None):
tree = ElementTree.fromstring(self._XMLDesc(0))
bridge_name = self.get_bridge_name(source, source_type)
xml_interface = """
@@ -784,8 +794,13 @@ class wvmInstance(wvmConnect):
-
- """ % (interface_type, mac_address, bridge_name, model)
+ """ % (interface_type, mac_address, bridge_name, model)
+ if nwfilter:
+ xml_interface += """
+
+ """ % nwfilter
+ xml_interface += """"""
+
if self.get_status() == 5:
devices = tree.find('devices')
elm_interface = ElementTree.fromstring(xml_interface)
@@ -793,20 +808,42 @@ class wvmInstance(wvmConnect):
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
+ def delete_network(self, mac_address):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ devices = tree.find('devices')
+ for interface in tree.findall('devices/interface'):
+ source = interface.find('mac')
+ if source.get('address', '') == mac_address:
+ source = None
+ devices.remove(interface)
+ new_xml = ElementTree.tostring(tree)
+ self._defineXML(new_xml)
+
def change_network(self, network_data):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
-
for num, interface in enumerate(tree.findall('devices/interface')):
net_source = network_data['net-source-' + str(num)]
net_source_type = network_data['net-source-' + str(num) + '-type']
net_mac = network_data['net-mac-' + str(num)]
+ net_filter = network_data['net-nwfilter-' + str(num)]
bridge_name = self.get_bridge_name(net_source, net_source_type)
if interface.get('type') == 'bridge':
source = interface.find('mac')
source.set('address', net_mac)
source = interface.find('source')
source.set('bridge', bridge_name)
+ source = interface.find('filterref')
+
+ if net_filter:
+ if source is not None: source.set('filter', net_filter)
+ else:
+ element = ElementTree.Element("filterref")
+ element.attrib['filter'] = net_filter
+ interface.append(element)
+ else:
+ if source is not None: interface.remove(source)
+
new_xml = ElementTree.tostring(tree)
self._defineXML(new_xml)
diff --git a/vrtManager/nwfilters.py b/vrtManager/nwfilters.py
new file mode 100644
index 0000000..58784b9
--- /dev/null
+++ b/vrtManager/nwfilters.py
@@ -0,0 +1,125 @@
+from vrtManager.connection import wvmConnect
+from xml.etree import ElementTree
+
+
+class wvmNWFilters(wvmConnect):
+ def get_nwfilter_info(self, name):
+ nwfilter = self.get_nwfilter(name)
+ xml = nwfilter.XMLDesc(0)
+ uuid = nwfilter.UUIDString()
+ return {'name': name, 'uuid': uuid, 'xml': xml}
+
+ def create_nwfilter(self, xml):
+ self.wvm.nwfilterDefineXML(xml)
+
+ def clone_nwfilter(self,name, cln_name):
+ nwfilter = self.get_nwfilter(name)
+ if nwfilter:
+ tree = ElementTree.fromstring(nwfilter.XMLDesc(0))
+ tree.set('name', cln_name)
+ uuid = tree.find('uuid')
+ tree.remove(uuid)
+ self.create_nwfilter(ElementTree.tostring(tree))
+
+
+class wvmNWFilter(wvmConnect):
+ def __init__(self, host, login, passwd, conn, nwfiltername):
+ wvmConnect.__init__(self, host, login, passwd, conn)
+ self.nwfilter = self.get_nwfilter(nwfiltername)
+
+ def _XMLDesc(self, flags):
+ return self.nwfilter.XMLDesc(flags)
+
+ def get_uuid(self):
+ return self.nwfilter.UUIDString()
+
+ def get_name(self):
+ return self.nwfilter.name()
+
+ def delete(self):
+ self.nwfilter.undefine()
+
+ def get_xml(self):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ uuid = tree.find('uuid')
+ tree.remove(uuid)
+ return ElementTree.tostring(tree)
+
+ def get_filter_refs(self):
+ refs = []
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ for ref in tree.findall("./filterref"):
+ refs.append(ref.get('filter'))
+ return refs
+
+ def get_rules(self):
+ rules = []
+
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ for r in tree.findall("./rule"):
+ rule_action = r.get('action')
+ rule_direction = r.get('direction')
+ rule_priority = r.get('priority')
+ rule_statematch = r.get('statematch')
+
+ rule_directives = r.find("./")
+ if rule_directives is not None:
+ rule_directives = ElementTree.tostring(rule_directives)
+
+ rule_info = {
+ "action": rule_action,
+ "direction": rule_direction,
+ "priority": rule_priority,
+ "statematch": rule_statematch,
+ "directives": rule_directives
+ }
+
+ rules.append(rule_info)
+
+ return rules
+
+ def delete_ref(self, name):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ for ref in tree.findall("./filterref"):
+ if name == ref.get('filter'):
+ tree.remove(ref)
+ break
+ return ElementTree.tostring(tree)
+
+ def delete_rule(self, action, direction, priority):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+
+ rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (action, direction, priority))
+ if rule_tree:
+ tree.remove(rule_tree[0])
+
+ return ElementTree.tostring(tree)
+
+ def add_ref(self, name):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ element = ElementTree.Element("filterref")
+ element.attrib['filter'] = name
+ tree.append(element)
+ return ElementTree.tostring(tree)
+
+ def add_rule(self, xml):
+ tree = ElementTree.fromstring(self._XMLDesc(0))
+ rule = ElementTree.fromstring(xml)
+
+ rule_action = rule.get('action')
+ rule_direction = rule.get('direction')
+ rule_priority = rule.get('priority')
+ rule_directives = rule.find("./")
+ rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (rule_action, rule_direction, rule_priority))
+
+ if rule_tree:
+ rule_tree[0].append(rule_directives)
+ else:
+ element = ElementTree.Element("rule")
+ element.attrib['action'] = rule_action
+ element.attrib['direction'] = rule_direction
+ element.attrib['priority'] = rule_priority
+ element.append(rule_directives)
+ tree.append(element)
+
+ return ElementTree.tostring(tree)
diff --git a/vrtManager/storage.py b/vrtManager/storage.py
index 7dc7515..21a1a7a 100644
--- a/vrtManager/storage.py
+++ b/vrtManager/storage.py
@@ -1,5 +1,6 @@
from vrtManager import util
from vrtManager.connection import wvmConnect
+from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
class wvmStorages(wvmConnect):
@@ -205,7 +206,7 @@ class wvmStorage(wvmConnect):
)
return vol_list
- def create_volume(self, name, size, vol_fmt='qcow2', metadata=False):
+ def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, owner=owner):
size = int(size) * 1073741824
storage_type = self.get_type()
alloc = size
@@ -222,8 +223,8 @@ class wvmStorage(wvmConnect):
- 107
- 107
+ %s
+ %s
0644
virt_image_t
@@ -232,10 +233,10 @@ class wvmStorage(wvmConnect):
- """ % (name, size, alloc, vol_fmt)
+ """ % (name, size, alloc, vol_fmt, owner, owner)
self._createXML(xml, metadata)
- def clone_volume(self, name, target_file, vol_fmt=None, metadata=False):
+ def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, owner=owner):
storage_type = self.get_type()
if storage_type == 'dir':
target_file += '.img'
@@ -250,8 +251,8 @@ class wvmStorage(wvmConnect):
- 107
- 107
+ %s
+ %s
0644
virt_image_t
@@ -260,5 +261,5 @@ class wvmStorage(wvmConnect):
- """ % (target_file, vol_fmt)
+ """ % (target_file, vol_fmt, owner,owner)
self._createXMLFrom(xml, vol, metadata)
diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template
index c75b1a2..7c78005 100644
--- a/webvirtcloud/settings.py.template
+++ b/webvirtcloud/settings.py.template
@@ -24,6 +24,7 @@ INSTALLED_APPS = (
'networks',
'storages',
'interfaces',
+ 'nwfilters',
'instances',
'secrets',
'logs',
@@ -105,6 +106,13 @@ WS_HOST = '0.0.0.0'
# Websock public port
WS_PUBLIC_HOST = None
+# Websock public port - default is 80 - uncomment 443 and comment 80 if you want to use vnc over https
+WS_PUBLIC_PORT = 80
+#WS_PUBLIC_PORT = 443
+
+# Websock pubic path
+WS_PUBLIC_PATH = '/novncd/'
+
# Websock SSL connection
WS_CERT = None
@@ -151,3 +159,5 @@ VIEW_INSTANCES_LIST_STYLE = 'grouped'
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
+# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
+INSTANCE_VOLUME_DEFAULT_OWNER = 0
diff --git a/webvirtcloud/urls.py b/webvirtcloud/urls.py
index d169386..2aa2c58 100644
--- a/webvirtcloud/urls.py
+++ b/webvirtcloud/urls.py
@@ -7,6 +7,8 @@ from secrets.views import secrets
from create.views import create_instance
from interfaces.views import interfaces, interface
from console.views import console
+from nwfilters.views import nwfilters, nwfilter
+from computes.views import computes
# from django.contrib import admin
urlpatterns = [
@@ -14,8 +16,13 @@ urlpatterns = [
url(r'^instances/$', instances, name='instances'),
url(r'^instance/', include('instances.urls')),
+ url(r'^instances/$', instances, name='instances'),
+
url(r'^accounts/', include('accounts.urls')),
+
url(r'^computes/', include('computes.urls')),
+ url(r'^computes/', computes, name='computes'),
+
url(r'^logs/', include('logs.urls')),
url(r'^datasource/', include('datasource.urls')),
@@ -25,9 +32,12 @@ urlpatterns = [
url(r'^compute/(?P
[0-9]+)/network/(?P[\w\-\.]+)/$', network, name='network'),
url(r'^compute/(?P[0-9]+)/interfaces/$', interfaces, name='interfaces'),
url(r'^compute/(?P[0-9]+)/interface/(?P[\w\-\.\:]+)/$', interface, name='interface'),
+ url(r'^compute/(?P[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
+ url(r'^compute/(?P[0-9]+)/nwfilter/(?P[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
url(r'^compute/(?P[0-9]+)/secrets/$', secrets, name='secrets'),
url(r'^compute/(?P[0-9]+)/create/$', create_instance, name='create_instance'),
+
url(r'^console/$', console, name='console'),
# (r'^admin/', include(admin.site.urls)),
]