Petr Šebek has uploaded a new change for review.

Change subject: Add IPv6 support to configNetwork
......................................................................

Add IPv6 support to configNetwork

This patch add IPv6 functionality to network configuration. We want to
run every device as dual stack device so IPConfig now uses IPv4 AND
IPv6(or one of them).

This patch was built on Hunt Xu's patch 11741.

Change-Id: I8ed056b683f0cb893b2edcf1ae673c64ce5cd18c
Signed-off-by: Petr Sebek <pse...@redhat.com>
---
M vdsm/configNetwork.py
M vdsm/netconf/ifcfg.py
M vdsm/netmodels.py
3 files changed, 157 insertions(+), 65 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/84/18284/1

diff --git a/vdsm/configNetwork.py b/vdsm/configNetwork.py
index 155d22a..ac458ed 100755
--- a/vdsm/configNetwork.py
+++ b/vdsm/configNetwork.py
@@ -35,6 +35,7 @@
 from netmodels import Bond
 from netmodels import Bridge
 from netmodels import IPv4
+from netmodels import IPv6
 from netmodels import IpConfig
 from netmodels import Nic
 from netmodels import Vlan
@@ -45,6 +46,8 @@
 def objectivizeNetwork(bridge=None, vlan=None, bonding=None,
                        bondingOptions=None, nics=None, mtu=None, ipaddr=None,
                        netmask=None, gateway=None, bootproto=None,
+                       ipv6addr=None, ipv6gateway=None, ipv6autoconf=None,
+                       dhcpv6=None,
                        _netinfo=None, configurator=None, blockingdhcp=None,
                        implicitBonding=None, defaultRoute=None, **opts):
     """
@@ -61,6 +64,10 @@
     :param netmask: IPv4 mask in dotted decimal format.
     :param gateway: IPv4 address in dotted decimal format.
     :param bootproto: protocol for getting IP config for the net, e.g., 'dhcp'
+    :param ipv6addr: IPv6 address in format address[/prefixlen].
+    :param ipv6gateway: IPv6 address in format address[/prefixlen].
+    :param ipv6autoconf: whether to use stateless autoconfiguration.
+    :param dhcpv6: whether to use DHCPv6.
     :param _netinfo: network information snapshot.
     :param configurator: instance to use to apply the network configuration.
     :param blockingdhcp: whether to acquire dhcp IP config in a synced manner.
@@ -103,9 +110,18 @@
     if topNetDev is None:
         raise ConfigNetworkError(ne.ERR_BAD_PARAMS, 'Network defined without'
                                  'devices.')
-    topNetDev.ip = IpConfig(inet=IPv4(ipaddr, netmask, gateway, defaultRoute),
-                            bootproto=bootproto,
-                            blocking=utils.tobool(blockingdhcp))
+    if ipv6addr is not None:
+        ipv6 = IPv6(ipv6addr, ipv6gateway, defaultRoute)
+    else:
+        ipv6 = None
+
+    if ipaddr is not None:
+        ipv4 = IPv4(ipaddr, netmask, gateway, defaultRoute)
+    else:
+        ipv4 = None
+    topNetDev.ip = IpConfig(inet4=ipv4, inet6=ipv6, bootproto=bootproto,
+                            blocking=utils.tobool(blockingdhcp),
+                            ipv6autoconf=ipv6autoconf, dhcpv6=dhcpv6)
     return topNetDev
 
 
@@ -150,7 +166,8 @@
 
 
 def addNetwork(network, vlan=None, bonding=None, nics=None, ipaddr=None,
-               netmask=None, prefix=None, mtu=None, gateway=None, force=False,
+               netmask=None, prefix=None, mtu=None, gateway=None,
+               ipv6addr=None, ipv6gateway=None, force=False,
                configurator=None, bondingOptions=None, bridged=True,
                _netinfo=None, qosInbound=None, qosOutbound=None, **options):
     nics = nics or ()
@@ -199,7 +216,8 @@
 
     netEnt = objectivizeNetwork(network if bridged else None, vlan, bonding,
                                 bondingOptions, nics, mtu, ipaddr, netmask,
-                                gateway, bootproto, _netinfo, configurator,
+                                gateway, bootproto, ipv6addr, ipv6gateway,
+                                _netinfo=_netinfo, configurator=configurator,
                                 defaultRoute=defaultRoute, **options)
 
     netEnt.configure(**options)
diff --git a/vdsm/netconf/ifcfg.py b/vdsm/netconf/ifcfg.py
index 8fafbd0..0d50a06 100644
--- a/vdsm/netconf/ifcfg.py
+++ b/vdsm/netconf/ifcfg.py
@@ -446,7 +446,8 @@
         return 'yes' if defaultRoute else 'no'
 
     def _createConfFile(self, conf, name, ipaddr=None, netmask=None,
-                        gateway=None, bootproto=None, mtu=None,
+                        gateway=None, bootproto=None, mtu=None, ipv6addr=None,
+                        ipv6gateway=None, ipv6autoconf=None, dhcpv6=None,
                         defaultRoute=None, onboot='yes', **kwargs):
         """ Create ifcfg-* file with proper fields per device """
         cfg = self.CONFFILE_HEADER + '\n'
@@ -470,6 +471,18 @@
         if defaultRoute:
             cfg = cfg + 'DEFROUTE=%s\n' % defaultRoute
         cfg += 'NM_CONTROLLED=no\n'
+        if ipv6addr or ipv6gateway or ipv6autoconf or dhcpv6:
+            cfg += 'IPV6INIT=yes\n'
+            ipv6autoconf = 'no'
+            if ipv6addr is not None:
+                cfg += 'IPV6ADDR=%s\n' % pipes.quote(ipv6addr)
+                if ipv6gateway is not None:
+                    cfg += 'IPV6_DEFAULTGW=%s\n' % pipes.quote(ipv6gateway)
+            elif dhcpv6:
+                cfg += 'DHCPV6C=yes\n'
+            if ipv6autoconf:
+                ipv6autoconf = 'yes'
+            cfg += 'IPV6_AUTOCONF=%s\n' % pipes.quote(ipv6autoconf)
         BLACKLIST = ['TYPE', 'NAME', 'DEVICE', 'bondingOptions',
                      'force', 'blockingdhcp',
                      'connectivityCheck', 'connectivityTimeout',
@@ -484,24 +497,26 @@
 
     def addBridge(self, bridge, **opts):
         """ Create ifcfg-* file with proper fields for bridge """
-        ipaddr, netmask, gateway, bootproto, _, defaultRoute = \
-            bridge.ipConfig
+        ipconfig = bridge.ipConfig
         conf = 'TYPE=Bridge\nDELAY=%s\n' % bridge.forwardDelay
-        self._createConfFile(conf, bridge.name, ipaddr, netmask, gateway,
-                             bootproto, bridge.mtu,
-                             self._toIfcfgFormat(defaultRoute), **opts)
+        self._createConfFile(conf, bridge.name, ipconfig.ipaddr,
+                             ipconfig.netmask, ipconfig.gateway,
+                             ipconfig.bootproto, bridge.mtu,
+                             self._toIfcfgFormat(self.defaultRoute),
+                             **opts)
 
     def addVlan(self, vlan, **opts):
         """ Create ifcfg-* file with proper fields for VLAN """
-        ipaddr, netmask, gateway, bootproto, _, defaultRoute = \
-            vlan.ipConfig
+        ipconfig = vlan.ipConfig
         conf = 'VLAN=yes\n'
         if vlan.bridge:
             conf += 'BRIDGE=%s\n' % pipes.quote(vlan.bridge.name)
 
-        self._createConfFile(conf, vlan.name, ipaddr, netmask, gateway,
-                             bootproto, vlan.mtu,
-                             self._toIfcfgFormat(defaultRoute), **opts)
+        self._createConfFile(conf, vlan.name, ipconfig.ipaddr,
+                             ipconfig.netmask, ipconfig.gateway,
+                             ipconfig.bootproto, vlan.mtu,
+                             self._toIfcfgFormat(ipconfig.defaultRoute),
+                             **opts)
 
     def addBonding(self, bond, **opts):
         """ Create ifcfg-* file with proper fields for bond """
@@ -509,10 +524,9 @@
         if bond.bridge:
             conf += 'BRIDGE=%s\n' % pipes.quote(bond.bridge.name)
 
-        ipaddr, netmask, gateway, bootproto, mtu, defaultRoute = \
-            self._getIfaceConfValues(bond, netinfo.NetInfo())
-        self._createConfFile(conf, bond.name, ipaddr, netmask, gateway,
-                             bootproto, mtu, defaultRoute, **opts)
+        confValues = self._getIfaceConfValues(bond, netinfo.NetInfo())
+        opts.update(confValues)
+        self._createConfFile(conf, bond.name, **opts)
 
         # create the bonding device to avoid initscripts noise
         if bond.name not in open(netinfo.BONDING_MASTERS).read().split():
@@ -530,31 +544,36 @@
         if nic.bond:
             conf += 'MASTER=%s\nSLAVE=yes\n' % pipes.quote(nic.bond.name)
 
-        ipaddr, netmask, gateway, bootproto, mtu, defaultRoute = \
-            self._getIfaceConfValues(nic, _netinfo)
-        self._createConfFile(conf, nic.name, ipaddr, netmask, gateway,
-                             bootproto, mtu, defaultRoute, **opts)
+        confValues = self._getIfaceConfValues(nic, _netinfo)
+        opts.update(confValues)
+        self._createConfFile(conf, nic.name, **opts)
 
     @staticmethod
     def _getIfaceConfValues(iface, _netinfo):
-        ipaddr, netmask, gateway, bootproto, _, defaultRoute = \
-            iface.ipConfig
-        defaultRoute = ConfigWriter._toIfcfgFormat(defaultRoute)
+        ipconfig = iface.ipConfig
+        defaultRoute = ConfigWriter._toIfcfgFormat(ipconfig.defaultRoute)
         mtu = iface.mtu
+        conf = {}
         if _netinfo.ifaceUsers(iface.name):
             confParams = netinfo.getIfaceCfg(iface.name)
-            if not ipaddr and bootproto != 'dhcp':
-                ipaddr = confParams.get('IPADDR', None)
-                netmask = confParams.get('NETMASK', None)
-                gateway = confParams.get('GATEWAY', None)
-                bootproto = confParams.get('BOOTPROTO', None)
+            if not ipconfig.inet4.ipaddr and ipconfig.bootproto != 'dhcp':
+                conf['ipaddr'] = confParams.get('IPADDR', None)
+                conf['netmask'] = confParams.get('NETMASK', None)
+                conf['gateway'] = confParams.get('GATEWAY', None)
+                conf['bootproto'] = confParams.get('BOOTPROTO', None)
             if defaultRoute is None:
-                defaultRoute = confParams.get('DEFROUTE', None)
+                conf['defaultRoute'] = confParams.get('DEFROUTE', None)
             if not iface.mtu:
                 mtu = confParams.get('MTU', None)
                 if mtu:
-                    mtu = int(mtu)
-        return ipaddr, netmask, gateway, bootproto, mtu, defaultRoute
+                    conf['mtu'] = int(mtu)
+            if confParams.get('IPV6INIT', 'no') == 'yes':
+                conf['ipv6autoconf'] = (confParams.get('IPV6_AUTOCONF', 'no')
+                                        == 'yes')
+                conf['ipv6gateway'] = confParams.get('IPV6_DEFAULTGW', None)
+                conf['dhcpv6'] = (confParams.get('DHCPV6C', 'no') == 'yes')
+                conf['ipv6addr'] = confParams.get('IPV6ADDR', None)
+        return conf
 
     def removeNic(self, nic):
         cf = netinfo.NET_CONF_PREF + nic
diff --git a/vdsm/netmodels.py b/vdsm/netmodels.py
index aad0c58..0acf456 100644
--- a/vdsm/netmodels.py
+++ b/vdsm/netmodels.py
@@ -16,7 +16,6 @@
 #
 # Refer to the README and COPYING files for full details of the license
 #
-from collections import namedtuple
 from contextlib import contextmanager
 import logging
 import os
@@ -30,9 +29,6 @@
 
 
 class NetDevice(object):
-    _ipConfig = namedtuple('ipConfig', ['ipaddr', 'netmask', 'gateway',
-                                        'bootproto', 'async', 'defaultRoute'])
-
     def __init__(self, name, configurator, ipconfig=None, mtu=None):
         self.name = name
         self.ip = ipconfig
@@ -48,14 +44,7 @@
 
     @property
     def ipConfig(self):
-        try:
-            ipaddr, netmask, gateway, bootproto, async, defaultRoute = \
-                self.ip.getConfig()
-        except AttributeError:
-            ipaddr = netmask = gateway = bootproto = async = defaultRoute = \
-                None
-        return self._ipConfig(ipaddr, netmask, gateway, bootproto, async,
-                              defaultRoute)
+        return self.ip.getConfig()
 
     @property
     def bridge(self):
@@ -322,25 +311,91 @@
             raise
 
 
-class IpConfig(object):
-    def __init__(self, inet, bootproto=None, blocking=False):
-        if inet.address and bootproto == 'dhcp':
-            raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Static and dynamic ip '
-                                     'configurations are mutually exclusive.')
-        self.inet = inet
-        self.bootproto = bootproto
-        self.async = bootproto == 'dhcp' and blocking
+class IPv6(object):
+    def __init__(self, address=None, gateway=None,
+                 defaultRoute=None):
+        if address:
+            self.validateAddress(address)
+            if gateway:
+                self.validateGateway(gateway)
+        elif gateway:
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Specified prefixlen '
+                                     'or gateway but not ip address.')
+        self.address = address
+        self.gateway = gateway
+        self.defaultRoute = defaultRoute
 
     def __repr__(self):
-        return 'IpConfig(%r, %s)' % (self.inet, self.bootproto)
+        return 'IPv6(%s, %s, %s)' % (self.address, self.gateway,
+                                     self.defaultRoute)
+
+    @classmethod
+    def validateAddress(cls, address):
+        addr = address.split('/', 1)
+        try:
+            socket.inet_pton(socket.AF_INET6, addr[0])
+        except socket.error:
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR, '%r is not a valid IPv6 '
+                                     'address.' % address)
+        if len(addr) == 2:
+            cls.validatePrefixlen(addr[1])
+
+    @classmethod
+    def validatePrefixlen(cls, prefixlen):
+        try:
+            prefixlen = int(prefixlen)
+            if prefixlen < 0 or prefixlen > 127:
+                raise ConfigNetworkError(ne.ERR_BAD_ADDR, '%r is not valid '
+                                         'IPv6 prefixlen.' % prefixlen)
+        except ValueError:
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR, '%r is not valid '
+                                     'IPv6 prefixlen.' % prefixlen)
+
+    @classmethod
+    def validateGateway(cls, gateway):
+        try:
+            cls.validateAddress(gateway)
+        except ConfigNetworkError as cne:
+            cne.message = '%r is not a valid IPv6 gateway.'
+            raise
+
+
+class IpConfig(object):
+    def __init__(self, inet4=None, inet6=None, bootproto=None, blocking=False,
+                 ipv6autoconf=None, dhcpv6=None):
+        if inet4 is None and inet6 is None:
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'You need to specify '
+                                     'IPv4 or IPv6 or both address.')
+        if ((inet4 and inet4.address and bootproto == 'dhcp') or
+           (inet6 and inet6.address and (ipv6autoconf or dhcpv6))):
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Static and dynamic ip '
+                                     'configurations are mutually exclusive.')
+        self.inet4 = inet4
+        self.inet6 = inet6
+        self.bootproto = bootproto
+        self.async = bootproto == 'dhcp' and blocking
+        self.ipv6autoconf = ipv6autoconf
+        self.dhcpv6 = dhcpv6
+
+    def __repr__(self):
+        return 'IpConfig(%r, %r, %s, %s, %s)' % (self.inet4, self.inet6,
+                                                 self.bootproto,
+                                                 self.ipv6autoconf,
+                                                 self.dhcpv6)
 
     def getConfig(self):
-        try:
-            ipaddr = self.inet.address
-            netmask = self.inet.netmask
-            gateway = self.inet.gateway
-            defaultRoute = self.inet.defaultRoute
-        except AttributeError:
-            ipaddr = netmask = gateway = defaultRoute = None
-        return ipaddr, netmask, gateway, self.bootproto, self.async, \
-            defaultRoute
+        config = {}
+        if self.inet4:
+            config['ipaddr'] = self.inet4.address
+            config['netmask'] = self.inet4.netmask
+            config['gateway'] = self.inet4.gateway
+            config['defaultRoute'] = self.inet4.defaultRoute
+        if self.inet6:
+            config['ipv6addr'] = self.inet6.address
+            config['ipv6gateway'] = self.inet6.gateway
+            config['ipv6defaultRoute'] = self.inet6.defaultRoute
+        config['bootproto'] = self.bootproto
+        config['async'] = self.async
+        config['ipv6autoconf'] = self.ipv6autoconf
+        config['dhcpv6'] = self.dhcpv6
+        return config


-- 
To view, visit http://gerrit.ovirt.org/18284
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8ed056b683f0cb893b2edcf1ae673c64ce5cd18c
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Petr Šebek <pse...@redhat.com>
_______________________________________________
vdsm-patches mailing list
vdsm-patches@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches

Reply via email to