From 38ae62d093811bde4bd8848deb124e3d607dcfcb Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 30 Oct 2019 11:02:38 +0300 Subject: [PATCH 1/5] define network with XML is related with network object. It is converted --- networks/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networks/views.py b/networks/views.py index 71a0b9a..0bc9c1d 100644 --- a/networks/views.py +++ b/networks/views.py @@ -161,7 +161,7 @@ def network(request, compute_id, pool): compute.login, compute.password, compute.type) - conn.define_network(edit_xml) + new_conn.define_network(edit_xml) if conn.is_active(): messages.success(request, _("Network XML is changed. Stop and start network to activate new config.")) else: From 568ff92449495b7dcec77e8f6724cf8bb3712480 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 30 Oct 2019 11:05:00 +0300 Subject: [PATCH 2/5] libvirt does not have the connection close reason contants anymore. pep8 conventions apply --- vrtManager/connection.py | 44 +++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/vrtManager/connection.py b/vrtManager/connection.py index 278232c..062ee91 100644 --- a/vrtManager/connection.py +++ b/vrtManager/connection.py @@ -124,16 +124,7 @@ class wvmConnection(object): # on server shutdown libvirt module gets freed before the close callbacks are called # so we just check here if it is still present if libvirt is not None: - if (reason == libvirt.VIR_CONNECT_CLOSE_REASON_ERROR): - self.last_error = 'connection closed: Misc I/O error' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_EOF): - self.last_error = 'connection closed: End-of-file from server' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_KEEPALIVE): - self.last_error = 'connection closed: Keepalive timer triggered' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_CLIENT): - self.last_error = 'connection closed: Client requested it' - else: - self.last_error = 'connection closed: Unknown error' + self.last_error = reason # prevent other threads from using the connection (in the future) self.connection = None @@ -255,11 +246,11 @@ class wvmConnectionManager(object): """ self._connections_lock.acquireRead() try: - if (host in self._connections): + if host in self._connections: connections = self._connections[host] for connection in connections: - if (connection.login == login and connection.passwd == passwd and connection.type == conn): + if connection.login == login and connection.passwd == passwd and connection.type == conn: return connection finally: self._connections_lock.release() @@ -278,13 +269,13 @@ class wvmConnectionManager(object): connection = self._search_connection(host, login, passwd, conn) - if (connection is None): + if connection is None: self._connections_lock.acquireWrite() try: # we have to search for the connection again after aquireing the write lock # as the thread previously holding the write lock may have already added our connection connection = self._search_connection(host, login, passwd, conn) - if (connection is None): + if connection is None: # create a new connection if a matching connection does not already exist connection = wvmConnection(host, login, passwd, conn) @@ -317,7 +308,7 @@ class wvmConnectionManager(object): socket_host.settimeout(1) if conn_type == CONN_SSH: if ':' in hostname: - LIBVIRT_HOST, PORT = (hostname).split(":") + LIBVIRT_HOST, PORT = hostname.split(":") PORT = int(PORT) else: PORT = SSH_PORT @@ -332,6 +323,7 @@ class wvmConnectionManager(object): except Exception as err: return err + connection_manager = wvmConnectionManager( settings.LIBVIRT_KEEPALIVE_INTERVAL if hasattr(settings, 'LIBVIRT_KEEPALIVE_INTERVAL') else 5, settings.LIBVIRT_KEEPALIVE_COUNT if hasattr(settings, 'LIBVIRT_KEEPALIVE_COUNT') else 5 @@ -368,7 +360,7 @@ class wvmConnect(object): minor = ver / 1000 ver = ver % 1000 release = ver - return "%s.%s.%s" % (major,minor,release) + return "%s.%s.%s" % (major, minor, release) def get_lib_version(self): ver = self.wvm.getLibVersion() @@ -432,7 +424,7 @@ class wvmConnect(object): for arch in ctx.xpath('/capabilities/guest/arch'): domain_types = arch.xpath('domain/@type') arch_name = arch.xpath('@name')[0] - result[arch_name]= domain_types + result[arch_name] = domain_types return result return util.get_xml_path(self.get_cap_xml(), func=hypervisors) @@ -446,7 +438,7 @@ class wvmConnect(object): for arch in ctx.xpath('/capabilities/guest/arch'): emulator = arch.xpath('emulator') arch_name = arch.xpath('@name')[0] - result[arch_name]= emulator + result[arch_name] = emulator return result return util.get_xml_path(self.get_cap_xml(), func=emulators) @@ -460,8 +452,8 @@ class wvmConnect(object): def get_bus_list(ctx): result = [] for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'): - if disk_enum.xpath("@name")[0] == "bus": - for values in disk_enum: result.append(values.text) + if disk_enum.xpath("@name")[0] == "bus": + for values in disk_enum: result.append(values.text) return result # return [ 'ide', 'scsi', 'usb', 'virtio' ] @@ -482,11 +474,11 @@ class wvmConnect(object): def get_image_formats(self): """Get available image formats""" - return [ 'raw', 'qcow', 'qcow2' ] + return ['raw', 'qcow', 'qcow2'] def get_file_extensions(self): """Get available image filename extensions""" - return [ 'img', 'qcow', 'qcow2' ] + return ['img', 'qcow', 'qcow2'] def get_video(self): """ Get available graphics video types """ @@ -496,7 +488,7 @@ class wvmConnect(object): if video_enum.xpath("@name")[0] == "modelType": for values in video_enum: result.append(values.text) return result - return util.get_xml_path(self.get_dom_cap_xml(),func=get_video_list) + return util.get_xml_path(self.get_dom_cap_xml(), func=get_video_list) def get_iface(self, name): return self.wvm.interfaceLookupByName(name) @@ -560,6 +552,7 @@ class wvmConnect(object): def get_host_instances(self, raw_mem_size=False): vname = {} + def get_info(doc): mem = util.get_xpath(doc, "/domain/currentMemory") mem = int(mem) / 1024 @@ -574,7 +567,7 @@ class wvmConnect(object): title = title if title else '' description = util.get_xpath(doc, "/domain/description") description = description if description else '' - return (mem, vcpu, title, description) + return mem, vcpu, title, description for name in self.get_instances(): dom = self.get_instance(name) xml = dom.XMLDesc(0) @@ -592,6 +585,7 @@ class wvmConnect(object): def get_user_instances(self, name): dom = self.get_instance(name) xml = dom.XMLDesc(0) + def get_info(ctx): mem = util.get_xpath(ctx, "/domain/currentMemory") mem = int(mem) / 1024 @@ -604,7 +598,7 @@ class wvmConnect(object): title = title if title else '' description = util.get_xpath(ctx, "/domain/description") description = description if description else '' - return (mem, vcpu, title, description) + return mem, vcpu, title, description (mem, vcpu, title, description) = util.get_xml_path(xml, func=get_info) return { 'name': dom.name(), From f3f4f0afe801d4bcdb44466e5d83ed4dbb1ae29b Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 30 Oct 2019 11:05:50 +0300 Subject: [PATCH 3/5] Fix typos. Code Inspection for pep8 conventions --- instances/views.py | 10 +++++++--- interfaces/views.py | 1 + nwfilters/views.py | 31 +++++++++++++++---------------- secrets/views.py | 1 + vrtManager/IPy.py | 16 ++++++++-------- vrtManager/instance.py | 8 +++++--- 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/instances/views.py b/instances/views.py index 1a1c5c0..474bfd2 100644 --- a/instances/views.py +++ b/instances/views.py @@ -73,6 +73,7 @@ def allinstances(request): def instances(request, compute_id): """ :param request: + :param compute_id :return: """ all_host_vms = {} @@ -224,9 +225,9 @@ def instance(request, compute_id, vname): def get_network_tuple(network_source_str): network_source_pack = network_source_str.split(":", 1) if len(network_source_pack) > 1: - return (network_source_pack[1], network_source_pack[0]) + return network_source_pack[1], network_source_pack[0] else: - return (network_source_pack[0], 'net') + return network_source_pack[0], 'net' def migrate_instance(new_compute, instance, live=False, unsafe=False, xml_del=False, offline=False): status = connection_manager.host_is_up(new_compute.type, new_compute.hostname) @@ -1104,10 +1105,13 @@ def instances_actions(request): return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path()) + @login_required def inst_graph(request, compute_id, vname): """ :param request: + :param compute_id: + :param vname: :return: """ json_blk = [] @@ -1298,7 +1302,7 @@ def delete_instance(instance, delete_disk=False): conn.delete() instance.delete() - print("Instance {} on compute {} sucessfully deleted".format(instance_name, compute.hostname)) + print("Instance {} on compute {} successfully deleted".format(instance_name, compute.hostname)) except libvirtError as lib_err: print("Error removing instance {} on compute {}".format(instance_name, compute.hostname)) diff --git a/interfaces/views.py b/interfaces/views.py index f71289c..acbf440 100644 --- a/interfaces/views.py +++ b/interfaces/views.py @@ -61,6 +61,7 @@ def interfaces(request, compute_id): def interface(request, compute_id, iface): """ :param request: + :param compute_id: :param iface: :return: """ diff --git a/nwfilters/views.py b/nwfilters/views.py index c0dd8d8..6f2529b 100644 --- a/nwfilters/views.py +++ b/nwfilters/views.py @@ -1,7 +1,6 @@ # -*- 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 _ @@ -19,6 +18,7 @@ from logs.views import addlogmsg def nwfilters(request, compute_id): """ :param request: + :param compute_id: :return: """ @@ -31,9 +31,9 @@ def nwfilters(request, compute_id): try: conn = wvmNWFilters(compute.hostname, - compute.login, - compute.password, - compute.type) + compute.login, + compute.password, + compute.type) if request.method == 'POST': if 'create_nwfilter' in request.POST: @@ -63,7 +63,7 @@ def nwfilters(request, compute_id): addlogmsg(request.user.username, compute.hostname, lib_err.message) if 'del_nwfilter' in request.POST: - name = request.POST.get('nwfiltername','') + name = request.POST.get('nwfiltername', '') msg = _("Deleting NWFilter: %s" % name) in_use = False nwfilter = conn.get_nwfilter(name) @@ -71,7 +71,6 @@ def nwfilters(request, compute_id): 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() @@ -90,10 +89,10 @@ def nwfilters(request, compute_id): if 'cln_nwfilter' in request.POST: - name = request.POST.get('nwfiltername','') + name = request.POST.get('nwfiltername', '') cln_name = request.POST.get('cln_name', name + '-clone') - conn.clone_nwfilter(name,cln_name) + conn.clone_nwfilter(name, cln_name) msg = _("Cloning NWFilter %s as %s" % (name, cln_name)) addlogmsg(request.user.username, compute.hostname, msg) @@ -122,14 +121,14 @@ def nwfilter(request, compute_id, nwfltr): try: nwfilter = wvmNWFilter(compute.hostname, - compute.login, - compute.password, - compute.type, - nwfltr) + compute.login, + compute.password, + compute.type, + nwfltr) conn = wvmNWFilters(compute.hostname, - compute.login, - compute.password, - compute.type) + compute.login, + compute.password, + compute.type) for nwf in conn.get_nwfilters(): nwfilters_all.append(conn.get_nwfilter_info(nwf)) @@ -208,4 +207,4 @@ def nwfilter(request, compute_id, nwfltr): except Exception as error_msg: error_messages.append(error_msg) - return render(request, 'nwfilter.html', locals()) \ No newline at end of file + return render(request, 'nwfilter.html', locals()) diff --git a/secrets/views.py b/secrets/views.py index 90e5e36..efc0a09 100644 --- a/secrets/views.py +++ b/secrets/views.py @@ -12,6 +12,7 @@ from libvirt import libvirtError def secrets(request, compute_id): """ :param request: + :param compute_id: :return: """ diff --git a/vrtManager/IPy.py b/vrtManager/IPy.py index 6dbdf6b..b157f3d 100644 --- a/vrtManager/IPy.py +++ b/vrtManager/IPy.py @@ -318,9 +318,9 @@ class IPint(object): (self._ipversion == 6 and self._prefixlen == 128): if self.NoPrefixForSingleIp: want = 0 - if want == None: + if want is None: want = self.WantPrefixLen - if want == None: + if want is None: want = 1 if want: if want == 2: @@ -354,7 +354,7 @@ class IPint(object): """ bits = _ipVersionToLen(self._ipversion) - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 0 ret = _intToBin(self.ip) return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen) @@ -370,7 +370,7 @@ class IPint(object): 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127' """ - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 1 if self._ipversion == 4: @@ -413,7 +413,7 @@ class IPint(object): 2001:658:22a:cafe:200:0:0:1 """ - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 1 if self._ipversion == 4: @@ -434,7 +434,7 @@ class IPint(object): 2001:0658:022a:cafe:0200:0000:0000:0001 """ - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 1 return intToIp(self.ip, self._ipversion) + self._printPrefix(wantprefixlen) @@ -448,7 +448,7 @@ class IPint(object): 0x20010658022acafe0200000000000001 """ - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 0 x = '0x%x' % self.ip @@ -463,7 +463,7 @@ class IPint(object): 42540616829182469433547762482097946625 """ - if self.WantPrefixLen == None and wantprefixlen == None: + if self.WantPrefixLen is None and wantprefixlen is None: wantprefixlen = 0 x = '%d' % self.ip diff --git a/vrtManager/instance.py b/vrtManager/instance.py index eb4147f..c929160 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -247,7 +247,7 @@ class wvmInstance(wvmConnect): def get_disk_devices(self): def disks(doc): result = [] - dev = volume = storage = src_file = None + dev = volume = storage = src_file = bus = None disk_format = used_size = disk_size = disk_cache = None for disk in doc.xpath('/domain/devices/disk'): @@ -288,7 +288,7 @@ class wvmInstance(wvmConnect): def get_media_devices(self): def disks(doc): result = [] - dev = volume = storage = None + dev = volume = storage = bus = None src_file = None for media in doc.xpath('/domain/devices/disk'): device = media.xpath('@device')[0] @@ -341,6 +341,7 @@ class wvmInstance(wvmConnect): def get_bootorder(self): boot_order = {} + type = target = None tree = ElementTree.fromstring(self._XMLDesc(0)) os = tree.find('os') boot = os.findall('boot') @@ -363,7 +364,7 @@ class wvmInstance(wvmConnect): devices = tree.find('devices') for dev in devices: - dev_target = dev_type = dev_device = dev_alias = None + dev_target = None boot_dev = dev.find('boot') if boot_dev is not None: idx = boot_dev.get('order') @@ -439,6 +440,7 @@ class wvmInstance(wvmConnect): disk.insert(2, src_media) return True + vol = None storages = self.get_storages(only_actives=True) for storage in storages: stg = self.get_storage(storage) From 0738ec7ec48710c34bd1a525b270ee2153dcaa07 Mon Sep 17 00:00:00 2001 From: catborise Date: Mon, 4 Nov 2019 12:03:13 +0300 Subject: [PATCH 4/5] adds bridge slave list to details of interface --- interfaces/templates/interface.html | 34 ++++++++++++++++++++++++++--- interfaces/views.py | 1 + vrtManager/interface.py | 30 +++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/interfaces/templates/interface.html b/interfaces/templates/interface.html index 1693d81..2ac89d9 100644 --- a/interfaces/templates/interface.html +++ b/interfaces/templates/interface.html @@ -38,12 +38,12 @@

{% trans "Interface" %}:

-

{% trans "IPv4" %}: ({% ifequal ipv4 None %}{% trans 'None' %}{% else %}{{ ipv4_type }}{% endifequal %})

-

{% trans "IPv6" %}: ({% ifequal ipv6 None %}{% trans 'None' %}{% else %}{{ ipv6_type }}{% endifequal %})

+

{% trans "IPv4" %} ({% ifequal ipv4 None %}{% trans 'None' %}{% else %}{{ ipv4_type }}{% endifequal %}):

+

{% trans "IPv6" %} ({% ifequal ipv6 None %}{% trans 'None' %}{% else %}{{ ipv6_type }}{% endifequal %}):

{% trans "MAC Adress" %}:

{% trans "Interface Type" %}:

{% ifequal itype 'bridge' %} -

{% trans "Bridge device" %}

+

{% trans "Bridge Device" %}:

{% endifequal %}

{% trans "Boot Mode" %}:

{% trans "State" %}:

@@ -69,5 +69,33 @@

+
+ {% ifequal itype 'bridge' %} + + + + + + + + + + + + + {% for iface in slave_ifaces %} + + + + + + + + {% endfor %} + +
{% trans 'Slaves' %}
{% trans 'MAC' %}{% trans 'Name' %}{% trans 'Type' %}{% trans 'Speed' %}{% trans 'State' %}
{{ iface.mac }}{{ iface.name }}{{ iface.type }}{{ iface.speed }}{{ iface.state }}
+ {% endifequal %} +
+ {% endblock %} \ No newline at end of file diff --git a/interfaces/views.py b/interfaces/views.py index acbf440..a747120 100644 --- a/interfaces/views.py +++ b/interfaces/views.py @@ -88,6 +88,7 @@ def interface(request, compute_id, iface): ipv6 = conn.get_ipv6() ipv6_type = conn.get_ipv6_type() bridge = conn.get_bridge() + slave_ifaces = conn.get_bridge_slave_ifaces() if request.method == 'POST': if 'stop' in request.POST: diff --git a/vrtManager/interface.py b/vrtManager/interface.py index c91aaf9..ceaea18 100644 --- a/vrtManager/interface.py +++ b/vrtManager/interface.py @@ -1,5 +1,6 @@ from vrtManager.connection import wvmConnect from vrtManager import util +from xml.etree import ElementTree from libvirt import VIR_INTERFACE_XML_INACTIVE @@ -119,9 +120,34 @@ class wvmInterface(wvmConnect): return int_ipv6_ip + '/' + int_ipv6_mask def get_bridge(self): + bridge = None if self.get_type() == 'bridge': - xml = self._XMLDesc() - return util.get_xml_path(xml, "/interface/bridge/interface/@name") + bridge = util.get_xml_path(self._XMLDesc(), "/interface/bridge/interface/@name") + for iface in self.get_bridge_slave_ifaces(): + if iface.get('state') == 'up' and iface.get('speed') is not 'unknown': + bridge = iface.get('name') + return bridge + return bridge + else: + return None + + def get_bridge_slave_ifaces(self): + ifaces = list() + if self.get_type() == 'bridge': + tree = ElementTree.fromstring(self._XMLDesc()) + for iface in tree.findall("./bridge/"): + address = state = speed = None + name = iface.get('name') + type = iface.get('type') + link = iface.find('link') + if link is not None: + state = link.get('state') + speed = link.get('speed') + mac = iface.find('mac') + if mac is not None: + address = mac.get('address') + ifaces.append({'name': name, 'type': type, 'state': state, 'speed': speed, 'mac': address}) + return ifaces else: return None From c5a96b7662e21ca21fc058e4809e6a6875b9087d Mon Sep 17 00:00:00 2001 From: catborise Date: Thu, 7 Nov 2019 10:33:36 +0300 Subject: [PATCH 5/5] Add IPv6 support. --- ...ss.html => modify_ipv4_fixed_address.html} | 21 +- .../templates/modify_ipv6_fixed_address.html | 53 +++++ networks/templates/network.html | 204 +++++++++++++++--- networks/views.py | 47 ++-- vrtManager/network.py | 194 ++++++++++------- 5 files changed, 391 insertions(+), 128 deletions(-) rename networks/templates/{modify_fixed_address.html => modify_ipv4_fixed_address.html} (85%) create mode 100644 networks/templates/modify_ipv6_fixed_address.html diff --git a/networks/templates/modify_fixed_address.html b/networks/templates/modify_ipv4_fixed_address.html similarity index 85% rename from networks/templates/modify_fixed_address.html rename to networks/templates/modify_ipv4_fixed_address.html index fd1c938..7c1addb 100644 --- a/networks/templates/modify_fixed_address.html +++ b/networks/templates/modify_ipv4_fixed_address.html @@ -1,16 +1,16 @@ {% load i18n %} {% if request.user.is_superuser %} - + - diff --git a/networks/templates/modify_ipv6_fixed_address.html b/networks/templates/modify_ipv6_fixed_address.html new file mode 100644 index 0000000..fcc7eff --- /dev/null +++ b/networks/templates/modify_ipv6_fixed_address.html @@ -0,0 +1,53 @@ +{% load i18n %} +{% if request.user.is_superuser %} + + + + + + +{% endif %} \ No newline at end of file diff --git a/networks/templates/network.html b/networks/templates/network.html index 46c79ab..c5d8cd5 100644 --- a/networks/templates/network.html +++ b/networks/templates/network.html @@ -68,6 +68,7 @@

+
@@ -93,31 +94,32 @@ -
+ +
-

{% trans "IPv4 Forwarding:" %}

-

{% trans "Network:" %}

-

{% trans "DHCP:" %}

+

{% trans "IPv4 Forwarding" %}:

+

{% trans "Network" %}:

{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %} -

{% trans "Start:" %}

-

{% trans "End:" %}

+

{% trans "DHCP" %}:

+

{% trans "Start" %}:

+

{% trans "End" %}:

{% endif %}

- {% ifequal ipv4_forward.0 'nat' %} + {% ifequal net_forward.0 'nat' %} {% trans "NAT" %} {% endifequal %} - {% ifequal ipv4_forward.0 'route' %} + {% ifequal net_forward.0 'route' %} {% trans "ROUTE" %} {% endifequal %} - {% ifequal ipv4_forward.0 'bridge' %} + {% ifequal net_forward.0 'bridge' %} {% trans "BRIDGE" %} {% endifequal %} - {% if not ipv4_forward.0 %} + {% if not net_forward.0 %} {% trans "ISOLATE" %} {% endif %}

@@ -137,6 +139,7 @@ {% else %}

+
- {% ifequal ipv4_forward.0 'nat' %} + {% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %} {% if state %} - {% include 'modify_fixed_address.html' %} + {% include 'modify_ipv4_fixed_address.html' %} {% endif %}
- +
- {% endifequal %} - {% if fixed_address %} + {% endif %} + {% if ipv4_fixed_address %}
@@ -175,7 +178,7 @@
- +
@@ -185,14 +188,132 @@ - {% for fix in fixed_address %} + {% for fix4 in ipv4_fixed_address %} {% csrf_token %} - - - + + + + + + {% endfor %} + +
{% trans "MAC" %}
- + + + +
+
+
+
+
+
+ + {% endif %} + +
+ +
+
+
+

{% trans "IPv6 Forwarding" %}:

+

{% trans "Network" %}:

+ {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %} +

{% trans "DHCP" %}:

+

{% trans "Start" %}:

+

{% trans "End" %}:

+ {% endif %} +
+
+

+ {% if not net_forward.0 %} + {% trans "ISOLATE" %} + {% else %} + {% trans "ROUTE" %} + {% endif %} +

+

{{ ipv6_network }}

+

+ {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %} + {% trans "ON" %} + {% else %} + {% trans "OFF" %} + {% endif %} +

+ {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %} +
{% csrf_token %} + {% if state %} +

{{ ipv6_dhcp_range_start }}

+

{{ ipv6_dhcp_range_end }}

+ {% else %} +

+

+ +
+ +
+ {% endif %} +
+ {% endif %} +
+
+ {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %} + {% if state %} + {% include 'modify_ipv6_fixed_address.html' %} + {% endif %} +
+ +
+ {% endif %} + {% if ipv6_fixed_address %} +
+
+
+
+ +
+
+ +
+
+ +
+ + +
+ + + + + + + + + + + {% for fix6 in ipv6_fixed_address %} + + {% csrf_token %} + + + +
{% trans "ID" %}{% trans "Address" %}{% trans "Name" %}{% trans "Action" %}
+