Change in vdsm[master]: [WIP] Network elements modeling as objects

asegurap at redhat.com asegurap at redhat.com
Tue Jan 22 16:58:03 UTC 2013


Antoni Segura Puimedon has uploaded a new change for review.

Change subject: [WIP] Network elements modeling as objects
......................................................................

[WIP] Network elements modeling as objects

This patch, and eventually patch series, works towards having an
object oriented representation of the network primitives to ease
the handling, verification and extension of the vdsm networking
code.

NOTE: It is currently just ideas that I'm having of what could be
represented and I welcome suggestions/corrections. As you can see
it is full of TODO. In this same patch there will be an addition
of a backend for performing the operations that we now do using
initscripts via brctl, /sys/class/net and iproute2. Since I don't
have it started yet, some of the code that will go there is scattered
across the objects in netmodels.

Change-Id: Ie6ded2ec4ab2f8ea9fdb83173cd8468caa92a2ae
Signed-off-by: Antoni S. Puimedon <asegurap at redhat.com>
---
A vdsm/netmodels.py
1 file changed, 258 insertions(+), 0 deletions(-)


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

diff --git a/vdsm/netmodels.py b/vdsm/netmodels.py
new file mode 100644
index 0000000..c65509a
--- /dev/null
+++ b/vdsm/netmodels.py
@@ -0,0 +1,258 @@
+# Copyright 2011-2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+import os
+import re
+import socket
+import struct
+
+from configNetwork import ConfigNetworkError
+import constants
+import neterrors as ne
+from storage.misc import execCmd
+
+
+class Bridge(object):
+    '''This class represents traditional kernel bridges.'''
+    MAX_BRIDGE_NAME_LEN = 15
+    ILLEGAL_BRIDGE_CHARS = frozenset(':. \t')
+
+    def __init__(self, name, ports=None, forward_delay=0, stp=None,
+                 priority=None):
+        # TODO: Handle the port searching/initialization/something.
+        self.validateBridgeName(name)
+        self.name = name
+        self.ports = ports
+        self.forward_delay = forward_delay
+        self.stp = stp
+        self.priority = priority
+        pass
+
+    @classmethod
+    def validateBridgeName(cls, name):
+        if not (name and len(name) <= cls.MAX_BRIDGE_NAME_LEN and
+                len(set(name) & cls.ILLEGAL_BRIDGE_CHARS) == 0 and
+                not name.startswith('-')):
+            raise ConfigNetworkError(ne.ERR_BAD_BRIDGE,
+                                     "Bridge name isn't valid: %r" % name)
+
+
+class Bond(object):
+    def __init__(self, name, slaves=None, **options):
+        # TODO: Handle the slave searching/initialization/something.
+        self.validateBondName(name)
+        self.name = name
+        self.slaves = slaves
+        if options:
+            self.validateBondingOptions(options)
+            # TODO: should the _validateInterNetworkCompatibility go here?
+            self.options = options
+        else:
+            self.option['mode'] = '802.3ad'
+            self.option['miimon'] = '150'
+
+    @staticmethod
+    def validateBondingName(name):
+        if not re.match('^bond[0-9]+$', name):
+            raise ConfigNetworkError(ne.ERR_BAD_BONDING,
+                                     '%r is not a valid bonding device name' %
+                                     name)
+
+    @staticmethod
+    def validateBondingOptions(bond, options):
+        'Example: BONDING_OPTS="mode=802.3ad miimon=150"'
+        # TODO: Probably move this into processing directly an options
+        # dictionary and make the conversion be somewhere else.
+        try:
+            for option in options.split():
+                key, value = option.split('=')
+                if not os.path.exists(
+                        '/sys/class/net/%(bond)s/bonding/%(key)s' %
+                        locals()):
+                    raise ConfigNetworkError(ne.ERR_BAD_BONDING, '%r is not a '
+                                             'valid bonding option' % key)
+        except ValueError:
+            raise ConfigNetworkError(ne.ERR_BAD_BONDING, 'Error parsing '
+                                     'bonding options: %r' % options)
+
+    # This is to be put outside this object.
+    # TODO: Move to the cmdline backend and put rollback mechanisms.
+    def apply(self):
+        '''Applies the configuration to the kernel networking stack.'''
+        # TODO: If the network already exists. It will currently give an
+        # IOError with errcode 17. Probably we should let it rise with extra
+        # info as _validateInterNetworkCompatibility should have prevented this
+        # from happening.
+        with open('/sys/class/net/bonding_masters', 'w') as bonds:
+            bonds.write('+%s' % self.name)
+
+        # Setting the bond options. TODO: Determine if we want to modify/add
+        # information to the IOError errno 22 (Invalid argument) we get when
+        # the option is wrong.
+        for option, value in self.options.iteritems():
+            with open('/sys/class/net/%s/bonding/%s' % (self.name, option),
+                      'w') as opt:
+                opt.write('option')
+
+        # Adding slaves. TODO: Figure out if it is possible to add them at
+        # once. Also determine how we should bubble up the IOError exception
+        # errno 19 (No such device).
+        for slave in self.slaves:
+            slave.link_down()
+            with open('/sys/class/net/%s/bonding/slaves' % self.name,
+                      'w') as bond:
+                bond.write('+%s' % slave.name)
+
+
+
+class Nic(object):
+    def __init__(self, name):
+        # TODO: Validate that the nic exists with netinfo.
+        self.name = name
+
+    # This will have to be made to use a specific backend and the current code
+    # move to the cmdline backend.
+    def link_down(self):
+        '''Takes the link of the specified nic down.'''
+        rc, out, err = execCmd([constants.EXT_IPROUTE, 'link', 'set', 'down',
+                                self.name], raw=False)
+        # TODO: Handle error cases. Possibly define something in vdsm/neterrors
+
+    # This will have to be made to use a specific backend and the current code
+    # move to the cmdline backend.
+    def link_up(self):
+        '''Takes the link of the specified nic down.'''
+        rc, out, err = execCmd([constants.EXT_IPROUTE, 'link', 'set', 'up',
+                                self.name], raw=False)
+        # TODO: Handle error cases. Possibly define something in vdsm/neterrors
+
+
+class IpConfig(object):
+    def __init__(self, inet, inet6):
+        '''Creates an instance with the IPv4 addresses in inet and the IPv6
+        addresses in inet6. inet and inet6 must be list of dictionaries like
+        so: {'address': '192.168.1.10', 'netmask': '24',
+             'gateway': '192.168.1.1'}'''
+        for net in inet:
+            self._validateInet(**net)
+        for net6 in inet6:
+            self._validateInet6(**net6)
+        # TODO: After this step, check that the gateways are all in a subnet
+        #       present in the current netinfo routes or in inet/inet6
+        self.inet = inet
+        self.inet6 = inet6
+
+        # This is a link property, does not belong here.
+        self.mtu = mtu
+
+    @classmethod
+    def validateIpv4Address(cls, ipAddr):
+        if not cls._validateIpAddress(ipAddr):
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR,
+                                     "Bad IP address: %r" % ipAddr)
+
+    @classmethod
+    def _validateInet(cls, address, netmask, gateway=None):
+        # Verification previously on configNetwork:_addNetworkValidation
+        if address:
+            if not netmask:
+                raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Must specify '
+                                         'netmask to configure ip for network')
+            cls.validateIpv6Address(address)
+            cls.validatev4Netmask(netmask)
+            if gateway:
+                cls.validatev4Gateway(gateway)
+        else:
+            if netmask or gateway:
+                raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Specified netmask '
+                                         'or gateway but not ip address')
+
+    @classmethod
+    def _validateInet6(cls, address, netmask, gateway=None):
+        # Verification previously on configNetwork:_addNetworkValidation
+        if address:
+            if not netmask:
+                raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Must specify '
+                                         'netmask to configure ip for network')
+            cls.validateIpv4Address(address)
+            cls.validatev6Netmask(netmask)
+            if gateway:
+                cls.validatev6Gateway(gateway)
+        else:
+            if netmask or gateway:
+                raise ConfigNetworkError(ne.ERR_BAD_ADDR, 'Specified netmask '
+                                         'or gateway but not ip address')
+
+    def _validateIpv4Address(address):
+        try:
+            socket.inet_pton(socket.AF_INET, address)
+        except socket.error:
+            return False
+        return True
+
+    def _validateIpv6Address(address):
+        try:
+            socket.inet_pton(socket.AF_INET6, address)
+        except socket.error:
+            return False
+        return True
+
+    @classmethod
+    def validatev4Netmask(cls, netmask):
+        return 0 <= netmask <= 32
+
+    def validatev6Netmask(cls, netmask):
+        # TODO: Normally it should be a number between 0 and 64, as the last
+        # 64 are normally for the host id. Thus, here we should decide what we
+        # enforce.
+        return 0 <= netmask <= 128
+
+    @classmethod
+    def validatev4Gateway(cls, gateway):
+        '''Validates the gateway form.'''
+        if not cls._validateIpv4Address(gateway):
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR,
+                                     "Bad gateway: %r" % gateway)
+
+    @classmethod
+    def validatev6Gateway(cls, gateway):
+        '''Validates the gateway form.'''
+        if not cls._validateIpv6Address(gateway):
+            raise ConfigNetworkError(ne.ERR_BAD_ADDR,
+                                     "Bad gateway: %r" % gateway)
+
+    # This is to be put outside this object.
+    # TODO: Move to the cmdline backend and put rollback mechanisms.
+    def apply(self, device):
+        '''Sets the addresses and routes.'''
+        for net in self.inet:
+            rc, out, err = execCmd(
+                [constants.EXT_IPROUTE, 'addr', 'add', net['address'] + '/' +
+                 net['netmask'], 'brd', '+', 'dev', device], raw=False)
+            # TODO: Do rollback if rc != 0
+
+        for net in self.inet6:
+            rc, out, err = execCmd(
+                [constants.EXT_IPROUTE, '-6', 'addr', 'add', net['address'] +
+                 '/' + net['netmask'], 'dev', device], raw=False)
+            # TODO: Do rollback if rc != 0
+
+        # TODO: use "ip route add default via %adress% " and
+        #   "ip -6 route add default via fe80::5054:ff:fedb:9209 dev %device%"
+        # Since only one IPv4 default gateway is supported, we could probably
+        # looping in inet4.


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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie6ded2ec4ab2f8ea9fdb83173cd8468caa92a2ae
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Antoni Segura Puimedon <asegurap at redhat.com>


More information about the vdsm-patches mailing list