From 38ae62d093811bde4bd8848deb124e3d607dcfcb Mon Sep 17 00:00:00 2001
From: catborise <catborise@yahoo.com>
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 <catborise@yahoo.com>
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 <catborise@yahoo.com>
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 <catborise@yahoo.com>
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 @@
     <div class="row">
         <div class="col-xs-6 col-sm-4">
             <p>{% trans "Interface" %}:</p>
-            <p>{% trans "IPv4" %}: ({% ifequal ipv4 None %}{% trans 'None' %}{% else %}{{ ipv4_type }}{% endifequal %})</p>
-            <p>{% trans "IPv6" %}: ({% ifequal ipv6 None %}{% trans 'None' %}{% else %}{{ ipv6_type }}{% endifequal %})</p>
+            <p>{% trans "IPv4" %} ({% ifequal ipv4 None %}{% trans 'None' %}{% else %}{{ ipv4_type }}{% endifequal %}):</p>
+            <p>{% trans "IPv6" %} ({% ifequal ipv6 None %}{% trans 'None' %}{% else %}{{ ipv6_type }}{% endifequal %}):</p>
             <p>{% trans "MAC Adress" %}:</p>
             <p>{% trans "Interface Type" %}:</p>
             {% ifequal itype 'bridge' %}
-                <p>{% trans "Bridge device" %}</p>
+                <p>{% trans "Bridge Device" %}:</p>
             {% endifequal %}
             <p>{% trans "Boot Mode" %}:</p>
             <p>{% trans "State" %}:</p>
@@ -69,5 +69,33 @@
                 </form>
             </p>
         </div>
+        <div class="col-sm-12">
+            {% ifequal itype 'bridge' %}
+            <table class="table table-bordered">
+            <caption>{% trans 'Slaves' %}</caption>
+                <thead>
+                <tr class="active">
+                    <td>{% trans 'MAC' %}</td>
+                    <td>{% trans 'Name' %}</td>
+                    <td>{% trans 'Type' %}</td>
+                    <td>{% trans 'Speed' %}</td>
+                    <td>{% trans 'State' %}</td>
+                </tr>
+                </thead>
+                <tbody>
+                {% for iface in slave_ifaces %}
+                    <tr>
+                        <td>{{ iface.mac }}</td>
+                        <td>{{ iface.name }}</td>
+                        <td>{{ iface.type }}</td>
+                        <td>{{ iface.speed }}</td>
+                        <td>{{ iface.state }}</td>
+                    </tr>
+                {% endfor %}
+                </tbody>
+            </table>
+            {% endifequal %}
+        </div>
     </div>
+
 {% 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 <catborise@yahoo.com>
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 %}
-    <a href="#AddFixedNet" type="button" class="btn btn-success pull-right" data-toggle="modal">
+    <a href="#AddFixedNet4" type="button" class="btn btn-success pull-right" data-toggle="modal">
         <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
     </a>
 
     <!-- Modal pool -->
-    <div class="modal fade" id="AddFixedNet" tabindex="-1" role="dialog" aria-labelledby="AddFixedNetLabel" aria-hidden="true">
+    <div class="modal fade" id="AddFixedNet4" tabindex="-1" role="dialog" aria-labelledby="AddFixedNet4Label" aria-hidden="true">
         <div class="modal-dialog">
             <div class="modal-content">
                 <div class="modal-header">
                     <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-                    <h4 class="modal-title">{% trans "Add Fixed Address" %}</h4>
+                    <h4 class="modal-title">{% trans "Add IPv4 Fixed Address" %}</h4>
                 </div>
                 <form  method="post" action="" role="form">{% csrf_token %}
                 <div class="modal-body">
@@ -21,10 +21,16 @@
                                 <input type="text" readonly class="form-control" name="subnet" value="{{ ipv4_network }}" required pattern="[0-9\/\.]+">
                             </div>
                         </div>
+                        <div class="form-group">
+                            <label class="col-sm-4 control-label">{% trans "MAC" %}</label>
+                            <div class="col-sm-6">
+                                <input type="text" class="form-control" name="mac" required pattern="[0-9\/\:]+">
+                            </div>
+                        </div>
                         <div class="form-group">
                             <label class="col-sm-4 control-label">{% trans "Name" %}</label>
                             <div class="col-sm-6">
-                                <input type="text" class="form-control" name="name" required pattern="[a-zA-Z0-9_]+">
+                                <input type="text" class="form-control" name="name" pattern="[a-zA-Z0-9_]+">
                             </div>
                         </div>
                         <div class="form-group">
@@ -33,15 +39,10 @@
                                 <input type="text" class="form-control" name="address" required pattern="[0-9\/\.]+">
                             </div>
                         </div>
-                        <div class="form-group">
-                            <label class="col-sm-4 control-label">{% trans "MAC" %}</label>
-                            <div class="col-sm-6">
-                                <input type="text" class="form-control" name="mac" required pattern="[0-9\/\:]+">
-                            </div>
-                        </div>
                     </div>
                 </div>
                  <div class="modal-footer">
+                     <input name="family" value="ipv4" hidden/>
                     <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
                     <button type="submit" class="btn btn-primary" name="modify_fixed_address">{% trans "Create" %}</button>
                 </div>
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 %}
+    <a href="#AddFixedNet6" type="button" class="btn btn-success pull-right" data-toggle="modal">
+        <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
+    </a>
+
+    <!-- Modal pool -->
+    <div class="modal fade" id="AddFixedNet6" tabindex="-1" role="dialog" aria-labelledby="AddFixedNet6Label" aria-hidden="true">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <h4 class="modal-title">{% trans "Add IPV6 Fixed Address" %}</h4>
+                </div>
+                <form  method="post" action="" role="form">{% csrf_token %}
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="form-group">
+                            <label class="col-sm-4 control-label">{% trans "Subnet Pool" %}</label>
+                            <div class="col-sm-6">
+                                <input type="text" readonly class="form-control" name="subnet" value="{{ ipv6_network }}" required pattern="[0-9\/\.]+">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-4 control-label">{% trans "ID" %}</label>
+                            <div class="col-sm-6">
+                                <input type="text" class="form-control" name="id" required pattern="[0-9a-dA-D\/\:]+">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-4 control-label">{% trans "Name" %}</label>
+                            <div class="col-sm-6">
+                                <input type="text" class="form-control" name="name" pattern="[a-zA-Z0-9_]+">
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <label class="col-sm-4 control-label">{% trans "Address" %}</label>
+                            <div class="col-sm-6">
+                                <input type="text" class="form-control" name="address" required pattern="[a-dA-D0-9\/\:]+">
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                 <div class="modal-footer">
+                     <input name="family" value="ipv6" hidden/>
+                    <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
+                    <button type="submit" class="btn btn-primary" name="modify_fixed_address">{% trans "Create" %}</button>
+                </div>
+                </form>
+            </div> <!-- /.modal-content -->
+        </div> <!-- /.modal-dialog -->
+    </div> <!-- /.modal -->
+{% 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 @@
             </p>
         </div>
     </div>
+
     <div class="row">
         <h3 class="page-header"></h3>
     </div>
@@ -93,31 +94,32 @@
             </div>
         </div>
     </div>
-    <div class="row">
+
+     <div class="row">
         <h3 class="page-header">{% trans "IPv4 Configuration" %}</h3>
     </div>
     <div class="row">
             <div class="col-xs-6 col-sm-4">
-                <p>{% trans "IPv4 Forwarding:" %}</p>
-                <p>{% trans "Network:" %}</p>
-                <p>{% trans "DHCP:" %}</p>
+                <p>{% trans "IPv4 Forwarding" %}:</p>
+                <p>{% trans "Network" %}:</p>
                 {% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
-                    <p>{% trans "Start:" %}</p>
-                    <p>{% trans "End:" %}</p>
+                    <p>{% trans "DHCP" %}:</p>
+                    <p>{% trans "Start" %}:</p>
+                    <p>{% trans "End" %}:</p>
                 {% endif %}
             </div>
              <div class="col-xs-6 col-sm-4">
                 <p>
-                    {% 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 %}
                 </p>
@@ -137,6 +139,7 @@
                         {%  else %}
                             <p><input name="range_start" value="{{ ipv4_dhcp_range_start }}"/></p>
                             <p><input name="range_end" value="{{ ipv4_dhcp_range_end }}"/></p>
+                            <input hidden name="family" value="ipv4"/>
                             <div class="col-xs-10 col-sm-8">
                                 <input type="submit" class="btn btn-primary btn-block" value="Apply"
                                        name="modify_dhcp_range"
@@ -147,15 +150,15 @@
                 {% endif %}
             </div>
         </div>
-    {% 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 %}
         <div class="row">
-            <h3 class="page-header">{% trans "Fixed Address" %}</h3>
+            <h3 class="page-header">{% trans "IPv4 Fixed Address" %}</h3>
         </div>
-    {% endifequal %}
-    {% if fixed_address %}
+    {% endif %}
+    {% if ipv4_fixed_address %}
         <div class="row">
             <div class="col-xs-12">
                 <div class="panel-group" id="accordion">
@@ -175,7 +178,7 @@
                                     <input type="button" class="btn btn-default" id="filter_button" value="Filter">
                                     <button type="button" class="btn btn-default" id="filter_clear">{% trans 'Clear' %}</button>
                                 </div>
-                                <table class="table table-hover">
+                                <table id="ipv4_table" class="table table-hover">
                                     <thead>
                                     <tr>
                                         <th style="text-align: center">{% trans "MAC" %}</th>
@@ -185,14 +188,132 @@
                                     </tr>
                                     </thead>
                                     <tbody style="text-align: center">
-                                    {% for fix in fixed_address %}
+                                    {% for fix4 in ipv4_fixed_address %}
                                         <tr>
                                             <form method="post" role="form">{% csrf_token %}
-                                                <td><label class="form-control" disabled="true">{{ fix.mac }}</label></td>
-                                                <td><input class="form-control" value="{{ fix.ip }}" name="address" /></td>
-                                                <td><input class="form-control" value="{{ fix.name }}" name="name" /></td>
+                                                <td><input class="form-control" value="{{ fix4.mac }}" name="mac" readonly/></td>
+                                                <td><input class="form-control" value="{{ fix4.ip }}" name="address" /></td>
+                                                <td><input class="form-control" value="{{ fix4.name }}" name="name" /></td>
                                                 <td>
-                                                    <input hidden name="mac" value="{{ fix.mac }}"/>
+                                                    <input hidden name="family" value="ipv4"/>
+                                                    <button type="submit" class="btn btn-sm btn-primary"
+                                                            name="modify_fixed_address"
+                                                            title="Edit entry" onclick="return confirm('{% trans "Are you sure?" %}')">
+                                                        <i class="glyphicon glyphicon-save"></i>
+                                                    </button>
+                                                    <button type="submit" class="btn btn-sm btn-danger"
+                                                            name="delete_fixed_address"
+                                                            title="Delete entry" onclick="return confirm('{% trans "Are you sure?" %}')">
+                                                        <i class="glyphicon glyphicon-trash"></i>
+                                                    </button>
+                                                </td>
+                                            </form>
+                                        </tr>
+                                    {% endfor %}
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    {% endif %}
+
+    <div class="row">
+        <h3 class="page-header">{% trans "IPv6 Configuration" %}</h3>
+    </div>
+    <div class="row">
+            <div class="col-xs-6 col-sm-4">
+                <p>{% trans "IPv6 Forwarding" %}:</p>
+                <p>{% trans "Network" %}:</p>
+                {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
+                    <p>{% trans "DHCP" %}:</p>
+                    <p>{% trans "Start" %}:</p>
+                    <p>{% trans "End" %}:</p>
+                {% endif %}
+            </div>
+             <div class="col-xs-6 col-sm-4">
+                <p>
+                    {% if not net_forward.0 %}
+                        {% trans "ISOLATE" %}
+                    {% else %}
+                        {% trans "ROUTE" %}
+                    {% endif %}
+                </p>
+                <p>{{ ipv6_network }}</p>
+                <p>
+                    {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
+                        <span class="text-success">{% trans "ON" %}</span>
+                    {% else %}
+                        <span class="text-danger">{% trans "OFF" %}</span>
+                    {% endif %}
+                </p>
+                {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
+                    <form method="post" role="form">{% csrf_token %}
+                        {% if state %}
+                            <p>{{ ipv6_dhcp_range_start }}</p>
+                            <p>{{ ipv6_dhcp_range_end }}</p>
+                        {%  else %}
+                            <p><input name="range_start" value="{{ ipv6_dhcp_range_start }}"/></p>
+                            <p><input name="range_end" value="{{ ipv6_dhcp_range_end }}"/></p>
+                            <input hidden name="family" value="ipv6"/>
+                            <div class="col-xs-10 col-sm-8">
+                                <input type="submit" class="btn btn-primary btn-block" value="Apply"
+                                       name="modify_dhcp_range"
+                                       title="Edit DHCP Range" onclick="return confirm('{% trans "Are you sure?" %}')"/>
+                            </div>
+                        {% endif %}
+                    </form>
+                {% endif %}
+            </div>
+        </div>
+     {% if ipv6_dhcp_range_start and ipv6_dhcp_range_end %}
+        {% if state %}
+            {% include 'modify_ipv6_fixed_address.html' %}
+        {% endif %}
+        <div class="row">
+            <h3 class="page-header">{% trans "IPv6 Fixed Address" %}</h3>
+        </div>
+    {% endif %}
+    {% if ipv6_fixed_address %}
+        <div class="row">
+            <div class="col-xs-12">
+                <div class="panel-group" id="accordion">
+                    <div class="panel panel-default">
+                        <div class="panel-heading">
+                            <a data-toggle="collapse" data-parent="#accordion" href="#collapseIPv6">
+                                {% trans 'Show' %}
+                            </a>
+                        </div>
+                        <div id="collapseIPv6" class="panel-collapse collapse">
+                            <div class="panel-body">
+
+                                <div class="input-append form-inline pull-right">
+                                    <div class="form-group">
+                                        <input type="text" class="form-control" id="filter_input_ipv6">
+                                    </div>
+                                    <input type="button" class="btn btn-default" id="filter_button_ipv6" value="Filter">
+                                    <button type="button" class="btn btn-default" id="filter_clear_ipv6">{% trans 'Clear' %}</button>
+                                </div>
+                                <table id="ipv6_table" class="table table-hover">
+                                    <thead>
+                                    <tr>
+                                        <th style="text-align: center">{% trans "ID" %}</th>
+                                        <th style="text-align: center">{% trans "Address" %}</th>
+                                        <th style="text-align: center">{% trans "Name" %}</th>
+                                        <th style="text-align: center">{% trans "Action" %}</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody style="text-align: center">
+                                    {% for fix6 in ipv6_fixed_address %}
+                                        <tr>
+                                            <form method="post" role="form">{% csrf_token %}
+                                                <td><input class="form-control" value="{{ fix6.id }}" name="id" readonly/></td>
+                                                <td><input class="form-control" value="{{ fix6.ip }}" name="address" /></td>
+                                                <td><input class="form-control" value="{{ fix6.name }}" name="name" /></td>
+                                                <td>
+                                                    <input hidden name="family" value="ipv6"/>
                                                     <button type="submit" class="btn btn-sm btn-primary"
                                                             name="modify_fixed_address"
                                                             title="Edit entry" onclick="return confirm('{% trans "Are you sure?" %}')">
@@ -229,15 +350,17 @@
         // add event button labeled "filter"
         $('#filter_button').click(function (event) {
             // get value
-            var filter_val = $('#filter_input').val();
+            let filter_val = $('#filter_input').val();
             if (filter_val == '') {
                 // show all
-                $('tbody tr').show();
+                $('#ipv4_table tbody tr').show();
             } else {
-                // show only matches
-                $('tbody tr:Contains(\'' + filter_val + '\')').show();
                 // hide non-matching items
-                $('tbody tr:not(:Contains(\'' + filter_val + '\'))').hide();
+                let row_not_contains4 = $('#ipv4_table tbody tr input:not([value*=\'' + filter_val + '\'])');
+                row_not_contains4.closest('tr').hide();
+                // show only matches
+                let row_contains4 =  $('#ipv4_table tbody tr input[value*=\'' + filter_val + '\']');
+                row_contains4.closest('tr').show();
             }
         });
         // add event button labeled "clear"
@@ -252,6 +375,35 @@
                 $('#filter_button').click();
             }
         });
+
+        // add event button labeled "filter"
+        $('#filter_button_ipv6').click(function (event) {
+            // get value
+            let filter_val = $('#filter_input_ipv6').val();
+            if (filter_val == '') {
+                // show all
+                $('#ipv6_table tbody tr').show();
+            } else {
+                // hide non-matching items
+                let row_not_contains6 = $('#ipv6_table tbody tr input:not([value*=\'' + filter_val + '\'])');
+                row_not_contains6.closest('tr').hide();
+                // show only matches
+                let row_contains6 =  $('#ipv6_table tbody tr input[value*=\'' + filter_val + '\']');
+                row_contains6.closest('tr').show();
+            }
+        });
+        // add event button labeled "clear"
+        $('#filter_clear_ipv6').click(function (event) {
+            $('#filter_input_ipv6').val('');
+            $('#filter_button_ipv6').click();
+        });
+
+        // trigger filter when enter key pressed
+        $('#filter_input_ipv6').keyup(function (event) {
+            if (event.keyCode == 13) {
+                $('#filter_button_ipv6').click();
+            }
+        });
     });
 </script>
 <script src="{% static "js/ace.js" %}"></script>
diff --git a/networks/views.py b/networks/views.py
index 0bc9c1d..427248a 100644
--- a/networks/views.py
+++ b/networks/views.py
@@ -86,11 +86,24 @@ def network(request, compute_id, pool):
         state = conn.is_active()
         device = conn.get_bridge_device()
         autostart = conn.get_autostart()
-        ipv4_forward = conn.get_ipv4_forward()
-        ipv4_dhcp_range_start = conn.get_ipv4_dhcp_range_start()
-        ipv4_dhcp_range_end = conn.get_ipv4_dhcp_range_end()
-        ipv4_network = conn.get_ipv4_network()
-        fixed_address = conn.get_mac_ipaddr()
+        net_forward = conn.get_network_forward()
+        dhcp_range_start = ipv4_dhcp_range_end = dict()
+
+        ip_networks = conn.get_ip_networks()
+        for family, ip_network in ip_networks.items():
+            if family == "ipv4":
+                ipv4_dhcp_range_start = conn.get_dhcp_range_start(family)
+                ipv4_dhcp_range_end = conn.get_dhcp_range_end(family)
+                ipv4_network = ip_network
+                ipv4_fixed_address = conn.get_dhcp_host_addr(family)
+            elif family == "ipv6":
+                ipv6_dhcp_range_start = conn.get_dhcp_range_start(family)
+                ipv6_dhcp_range_end = conn.get_dhcp_range_end(family)
+                ipv6_network = ip_network
+                ipv6_fixed_address = conn.get_dhcp_host_addr(family)
+            else:
+                raise Exception("Unknown Network Family")
+
         xml = conn._XMLDesc(0)
     except libvirtError as lib_err:
         error_messages.append(lib_err)
@@ -130,26 +143,34 @@ def network(request, compute_id, pool):
         if 'modify_fixed_address' in request.POST:
             name = request.POST.get('name', '')
             address = request.POST.get('address', '')
-            mac = request.POST.get('mac', '')
+            family = request.POST.get('family', 'ipv4')
+
+            if family == 'ipv4':
+                mac_duid = request.POST.get('mac', '')
+            if family == 'ipv6':
+                mac_duid = request.POST.get('id', '')
+
             try:
-                ret_val = conn.modify_fixed_address(name, address, mac)
-                messages.success(request, "Fixed Address Operation Completed.")
+                ret_val = conn.modify_fixed_address(name, address, mac_duid, family)
+                messages.success(request, "{} Fixed Address Operation Completed.".format(family))
                 return HttpResponseRedirect(request.get_full_path())
             except libvirtError as lib_err:
                 error_messages.append(lib_err.message)
             except ValueError as val_err:
                 error_messages.append(val_err.message)
         if 'delete_fixed_address' in request.POST:
-            mac = request.POST.get('mac', '')
-            conn.delete_fixed_address(mac)
-            messages.success(request, "Fixed Address is Deleted.")
+            ip = request.POST.get('address', '')
+            family = request.POST.get('family', 'ipv4')
+            conn.delete_fixed_address(ip, family)
+            messages.success(request, "{} Fixed Address is Deleted.".format(family))
             return HttpResponseRedirect(request.get_full_path())
         if 'modify_dhcp_range' in request.POST:
             range_start = request.POST.get('range_start', '')
             range_end = request.POST.get('range_end', '')
+            family = request.POST.get('family', 'ipv4')
             try:
-                conn.modify_dhcp_range(range_start, range_end)
-                messages.success(request, "DHCP Range is Changed.")
+                conn.modify_dhcp_range(range_start, range_end, family)
+                messages.success(request, "{} DHCP Range is Changed.".format(family))
                 return HttpResponseRedirect(request.get_full_path())
             except libvirtError as lib_err:
                 error_messages.append(lib_err.message)
diff --git a/vrtManager/network.py b/vrtManager/network.py
index 971cc66..ded82c3 100644
--- a/vrtManager/network.py
+++ b/vrtManager/network.py
@@ -1,7 +1,7 @@
 from vrtManager import util
 from vrtManager.IPy import IP
 from vrtManager.connection import wvmConnect
-from xml.etree import ElementTree
+from lxml import etree
 from libvirt import VIR_NETWORK_SECTION_IP_DHCP_HOST
 from libvirt import VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_UPDATE_COMMAND_MODIFY
 from libvirt import VIR_NETWORK_UPDATE_AFFECT_LIVE, VIR_NETWORK_UPDATE_AFFECT_CONFIG
@@ -77,6 +77,7 @@ class wvmNetwork(wvmConnect):
     def __init__(self, host, login, passwd, conn, net):
         wvmConnect.__init__(self, host, login, passwd, conn)
         self.net = self.get_network(net)
+        self.parent_count = len(self.get_ip_networks())
 
     def get_name(self):
         return self.net.name()
@@ -111,128 +112,163 @@ class wvmNetwork(wvmConnect):
     def delete(self):
         self.net.undefine()
 
-    def update(self, command, section, parentIndex, xml, flags=0):
+    def update(self, command, section, xml, parentIndex, flags=0):
         return self.net.update(command, section, parentIndex, xml, flags)
 
-    def get_ipv4_network(self):
+    def get_ip_networks(self):
+        ip_networks = dict()
         xml = self._XMLDesc(0)
         if util.get_xml_path(xml, "/network/ip") is None:
             return None
-        addrStr = util.get_xml_path(xml, "/network/ip/@address")
-        netmaskStr = util.get_xml_path(xml, "/network/ip/@netmask")
-        prefix = util.get_xml_path(xml, "/network/ip/@prefix")
+        tree = etree.fromstring(xml)
+        ips = tree.findall('.ip')
+        for ip in ips:
+            address_str = ip.get('address')
+            netmask_str = ip.get('netmask')
+            prefix = ip.get('prefix')
+            family = ip.get('family', 'ipv4')
+            base = 32 if family == 'ipv4' else 128
+            if prefix:
+                prefix = int(prefix)
+                binstr = ((prefix * "1") + ((base - prefix) * "0"))
+                netmask_str = str(IP(int(binstr, base=2)))
 
-        if prefix:
-            prefix = int(prefix)
-            binstr = ((prefix * "1") + ((32 - prefix) * "0"))
-            netmaskStr = str(IP(int(binstr, base=2)))
+            if netmask_str:
+                netmask = IP(netmask_str)
+                gateway = IP(address_str)
+                network = IP(gateway.int() & netmask.int())
+                netmask_str = netmask_str if family == 'ipv4' else str(prefix)
+                ret = IP(str(network) + "/" + netmask_str)
+            else:
+                ret = IP(str(address_str))
+            ip_networks[family] = ret
+        return ip_networks
 
-        if netmaskStr:
-            netmask = IP(netmaskStr)
-            gateway = IP(addrStr)
-            network = IP(gateway.int() & netmask.int())
-            ret = IP(str(network) + "/" + netmaskStr)
-        else:
-            ret = IP(str(addrStr))
-
-        return ret
-
-    def get_ipv4_forward(self):
+    def get_network_forward(self):
         xml = self._XMLDesc(0)
         fw = util.get_xml_path(xml, "/network/forward/@mode")
-        forwardDev = util.get_xml_path(xml, "/network/forward/@dev")
-        return [fw, forwardDev]
+        forward_dev = util.get_xml_path(xml, "/network/forward/@dev")
+        return [fw, forward_dev]
 
-    def get_ipv4_dhcp_range(self):
+    def get_dhcp_range(self, family='ipv4'):
         xml = self._XMLDesc(0)
-        dhcpstart = util.get_xml_path(xml, "/network/ip/dhcp/range[1]/@start")
-        dhcpend = util.get_xml_path(xml, "/network/ip/dhcp/range[1]/@end")
+        if family == 'ipv4':
+            dhcpstart = util.get_xml_path(xml, "/network/ip[not(@family='ipv6')]/dhcp/range[1]/@start")
+            dhcpend = util.get_xml_path(xml, "/network/ip[not(@family='ipv6')]/dhcp/range[1]/@end")
+        if family == 'ipv6':
+            dhcpstart = util.get_xml_path(xml, "/network/ip[@family='ipv6']/dhcp/range[1]/@start")
+            dhcpend = util.get_xml_path(xml, "/network/ip[@family='ipv6']/dhcp/range[1]/@end")
+
         if not dhcpstart or not dhcpend:
             return None
 
         return [IP(dhcpstart), IP(dhcpend)]
 
-    def get_ipv4_dhcp_range_start(self):
-        dhcp = self.get_ipv4_dhcp_range()
+    def get_dhcp_range_start(self, family='ipv4'):
+        dhcp = self.get_dhcp_range(family)
         if not dhcp:
             return None
-
         return dhcp[0]
 
-    def get_ipv4_dhcp_range_end(self):
-        dhcp = self.get_ipv4_dhcp_range()
+    def get_dhcp_range_end(self, family='ipv4'):
+        dhcp = self.get_dhcp_range(family)
         if not dhcp:
             return None
-
         return dhcp[1]
 
     def can_pxe(self):
         xml = self._XMLDesc(0)
-        forward = self.get_ipv4_forward()[0]
+        forward = self.get_network_forward()[0]
         if forward and forward != "nat":
             return True
         return bool(util.get_xml_path(xml, "/network/ip/dhcp/bootp/@file"))
 
-    def get_mac_ipaddr(self):
-        def network(doc):
-            result = []
-            for net in doc.xpath('/network/ip/dhcp/host'):
-                ip = net.xpath('@ip')[0]
-                mac = net.xpath('@mac')[0]
-                name = net.xpath('@name')
-                name = name[0] if name else ""
+    def get_dhcp_host_addr(self, family='ipv4'):
+        result = list()
+        tree = etree.fromstring(self._XMLDesc(0))
 
-                result.append({'ip': ip, 'mac': mac, 'name': name})
-            return result
+        for ipdhcp in tree.findall("./ip"):
+            if family == 'ipv4':
+                if ipdhcp.get('family') is None:
+                    hosts = ipdhcp.findall('./dhcp/host')
+                    for host in hosts:
+                        ip = host.get('ip')
+                        mac = host.get('mac')
+                        name = host.get('name','')
+                        result.append({'ip': ip, 'mac': mac, 'name': name})
+                    return result
+                else:
+                    continue
+            if family == 'ipv6':
+                hosts = tree.xpath("./ip[@family='ipv6']/dhcp/host")
+                for host in hosts:
+                    ip = host.get('ip')
+                    id = host.get('id')
+                    name = host.get('name','')
+                    result.append({'ip': ip, 'id': id, 'name': name})
+                return result
 
-        return util.get_xml_path(self._XMLDesc(0), func=network)
-    
-    def modify_fixed_address(self, name, address, mac):
-        util.validate_macaddr(mac)
-        if name:
-            new_xml = '<host mac="{}" name="{}" ip="{}"/>'.format(mac, name, IP(address))
-        else:
-            new_xml = '<host mac="{}" ip="{}"/>'.format(mac, IP(address))
-        new_host_xml = ElementTree.fromstring(new_xml)
+    def modify_dhcp_range(self, range_start, range_end, family='ipv4'):
+        if not self.is_active():
+            tree = etree.fromstring(self._XMLDesc(0))
+            if family == 'ipv4':
+                range = tree.xpath("./ip[not(@family='ipv6')]/dhcp/range")
+            if family == 'ipv6':
+                range = tree.xpath("./ip[@family='ipv6']/dhcp/range")
+            range[0].set('start', range_start)
+            range[0].set('end', range_end)
+            self.wvm.networkDefineXML(etree.tostring(tree))
 
-        tree = ElementTree.fromstring(self._XMLDesc(0))
-        hosts = tree.findall("./ip/dhcp/host")
+    def delete_fixed_address(self, ip, family='ipv4'):
+        tree = etree.fromstring(self._XMLDesc(0))
+        if family == 'ipv4':
+            hosts = tree.xpath("/network/ip[not(@family='ipv6')]/dhcp/host")
+            parent_index = self.parent_count - 2
+        if family == 'ipv6':
+            hosts = tree.xpath("/network/ip[@family='ipv6']/dhcp/host")
+            parent_index = self.parent_count - 1
+        for h in hosts:
+            if h.get('ip') == ip:
+                if family == 'ipv4':
+                    new_xml = '<host mac="{}" name="{}" ip="{}"/>'.format(h.get('mac'), h.get('name'), ip)
+                if family == 'ipv6':
+                    new_xml = '<host id="{}" name="{}" ip="{}"/>'.format(h.get('id'), h.get('name'), ip)
+
+                self.update(VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_SECTION_IP_DHCP_HOST,
+                            new_xml,
+                            parent_index,
+                            VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG)
+                break
+
+    def modify_fixed_address(self, name, address, mac_duid, family='ipv4'):
+        tree = etree.fromstring(self._XMLDesc(0))
+        if family == 'ipv4':
+            new_xml = '<host mac="{}" {} ip="{}"/>'.format(mac_duid, 'name="' + name + '"' if name else '', IP(address))
+            hosts = tree.xpath("./ip[not(@family='ipv6')]/dhcp/host")
+            compare_var = 'mac'
+            parent_index = self.parent_count - 2
+        if family == 'ipv6':
+            new_xml = '<host id="{}" {} ip="{}"/>'.format(mac_duid, 'name="' + name + '"' if name else '', IP(address))
+            hosts = tree.xpath("./ip[@family='ipv6']/dhcp/host")
+            compare_var = 'id'
+            parent_index = self.parent_count - 1
+        new_host_xml = etree.fromstring(new_xml)
 
         host = None
         for h in hosts:
-            if h.get('mac') == mac:
+            if h.get(compare_var) == mac_duid:
                 host = h
                 break
         if host is None:
-            self.update(VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
+            self.update(VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_SECTION_IP_DHCP_HOST, new_xml,
+                        parent_index,
                         VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG)
         else:
             # change the host
             if host.get('name') == new_host_xml.get('name') and host.get('ip') == new_host_xml.get('ip'):
                 return False
             else:
-                self.update(VIR_NETWORK_UPDATE_COMMAND_MODIFY, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
+                self.update(VIR_NETWORK_UPDATE_COMMAND_MODIFY, VIR_NETWORK_SECTION_IP_DHCP_HOST, new_xml,
+                            parent_index,
                             VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG)
 
-    def delete_fixed_address(self, mac):
-        util.validate_macaddr(mac)
-        tree = ElementTree.fromstring(self._XMLDesc(0))
-        hosts = tree.findall("./ip/dhcp/host")
-
-        for h in hosts:
-            if h.get('mac') == mac:
-                new_xml = '<host mac="{}" name="{}" ip="{}"/>'.format(mac, h.get('name'), h.get('ip'))
-                self.update(VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
-                            VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG)
-                break
-
-    def modify_dhcp_range(self, range_start, range_end):
-        if not self.is_active():
-            new_range = '<range start="{}" end="{}"/>'.format(range_start, range_end)
-            tree = ElementTree.fromstring(self._XMLDesc(0))
-            dhcp = tree.find("./ip/dhcp")
-            old_range = dhcp.find('range')
-            dhcp.remove(old_range)
-            dhcp.append(ElementTree.fromstring(new_range))
-
-            self.wvm.networkDefineXML(ElementTree.tostring(tree))