Commits

Moises Henriquez committed 7ff839c

Additions to Networking backend

  • Participants
  • Parent commits a5f6e91

Comments (0)

Files changed (2)

src/tests/test_NETWORKING.py

 
 class NetworkingTests(unittest.TestCase):
     def setUp(self):
-        return
+        self.addCleanup(self._cleanup)
+        self.assertTrue(hasattr(NETWORKING, 'NetConfModel'))
+        self.hostname = 'foo.host.vnet'
+        self.host_file = '/tmp/hostnamefile'
+        self.model = NETWORKING.NetConfModel(host_file=self.host_file)
     
-    def testModuleAttributes(self):
-        self.assertTrue(hasattr(NETWORKING, 'NetConfModel'))
+    def tearDown(self):
+        return self._cleanup()
     
-    def testReadHostName(self):
-        self.assertEqual(1, 0)
+    def _cleanup(self):
+        if os.path.exists(self.host_file):
+            os.remove(self.host_file)
     
-    def testSetHostName(self):
-        self.assertEqual(1, 0)
+    def testCreateNic(self):
+        # Create a fake nic object
+        mynic = NETWORKING.Nic('wlan10')
+        # The config file should not exist
+        self.assertTrue(not os.path.exists(mynic.inet.fpath))
+        self.assertEqual(mynic.inet.config['DEVICE'], mynic.devname)
+        # Disabling this nic should raise an exception because the
+        # config file does not exist
+        self.assertRaises(AssertionError, mynic.disable)
     
-    def testGetNicList(self):
-        self.assertEqual(1, 0)
+    def testHostnameWriteRead(self):
+        # writing an empty hostname raise exception
+        self.model.hostname = ""
+        self.assertRaises(AssertionError, self.model.set_hostname)
+        self.model.hostname = None
+        self.assertRaises(AssertionError, self.model.set_hostname)
+        # Passing an argument to set_hostname will raise TypeError
+        self.assertRaises(TypeError, self.model.set_hostname, 'foo')
+        
+        # write the hostname
+        self.model.hostname = self.hostname
+        self.model.set_hostname()
+        readname = self.model.get_hostname()
+        return self.assertEqual(readname, self.hostname)
+
 
 if __name__ == '__main__':
     assert os.geteuid() == 0, "Only root is allowed to run these tests."""

src/vasm/backend/NETWORKING.py

 from netifaces import *
 import os
 import logging
+import shutil
 from utils import get_popen
 import SERVICES
 
                         Usually somewhere in /etc/rc.d/init.d """
         
     def __init__(self, name=None, bin_path=None, init_script=None, 
-            service_script=None):
+            service_script=None, sessionscript = None):
         self.name = name
         self.bin_path = bin_path
         self.init_script = init_script
         self.service_script = service_script
+        self.sessionscript = sessionscript
+        
+        self.enabled_autostart = '/etc/xdg/autostart'
+        self.disabled_autostart = '/etc/xdg/autostart-disabled'
+        self.sessionfile = os.path.split(self.sessionscript)[-1]
     
     def _run_command(self, cmd):
         proc = get_popen(cmd)
         stdout, stderr = proc.communicate()
         code = proc.returncode
-        assert code == 0, stderr.strip()
+        assert code == 0, stderr.strip()    
     
     def enable(self):
         """ Make the init_script executable """
+        self.enable_session_script()
         return self._run_command(['chmod','+x', self.init_stript])
     
     def disable(self):
         """ Make the init_script not executable """
+        self.disable_session_script()
         return self._run_command(['chmod', '-x', self.init_stript])
     
+    def enable_session_script(self):
+        """ Enable the session startup script.  This will allow the desktop
+            to automatically start this daemons panel applets."""
+        dpath = os.path.join(self.disabled_autostart, self.sessionfile)
+        epath = os.path.join(self.enabled_autostart, self.sessionfile)
+        if os.path.exists(dpath):
+            shutil.move(dpath, epath)
+        return
+    
+    def disable_session_script(self):
+        """ Disable the session startup scripts.  This will avoid the desktop
+            showing error messages about not being able to connect to this
+            daemons backend."""
+        # Make sure the disabled dir exists
+        if not os.path.isdir(self.disabled_autostart):
+            os.makedirs(self.disabled_autostart)
+        dpath = os.path.join(self.disabled_autostart, self.sessionfile)
+        epath = os.path.join(self.enabled_autostart, self.sessionfile)
+        if os.path.exists(epath):
+            return shutil.move(epath, dpath)
+        return
+    
+    def check_status(self):
+        """ Return the status of the daemon. 
+        True = Daemon is enabled.
+        False = Daemon is disabled."""
+        if self._get_rcfile_status() or \
+            os.path.exists(os.path.join(self.enabled_autostart, self.sessionfile)):
+            return True
+        return False
+    
+    def _get_rcfile_status(self):
+        """ Returns True if self.init_script is executable.  False otherwise """
+        return os.access(self.init_script, os.X_OK)
+    
     def disable_service(self):
         """ disable the service for all runlevels """
         assert self.service_script is not None, \
         macaddr - hardware MAC address
     """
     def __init__(self, devname=None,
-            ip_addr = None,
-            netmask = None,
-            bcast = None,
+            ip_addr = '127.0.0.1',
+            netmask = '255.255.255.0',
+            bcast = '',
             macaddr = None,
-            ):
+            scripts_loc = '/etc/rc.d'):
         self.devname = devname
         self.ip_addr = ip_addr
         self.netmask = netmask
         self.broadcast = bcast
         self.macaddr = macaddr
+        self.scripts_location = scripts_loc
+        self.inet = Inet(
+            fpath = self.findConfig(), nic = self)
+    
+    def _get_next_available_config_file(self):
+        """ Return a path to the next available config file """
+        for x in range(1,10):
+            initpath = os.path.join(self.scripts_location,
+                'rc.inet%s'% x)
+            if not os.path.exists(initpath):
+                return initpath
+        # Should never return None unless the box has a ridiculous
+        # number of network adaptors.
+        return None
+    
+    def findConfig(self):
+        """ Locate the configuration file that configures this adaptor"""
+        for file in os.listdir(self.scripts_location):
+            if file.startswith('rc.inet') and \
+                '.conf' not in file and not file.endswith('.new'):
+                fpath = os.path.join(self.scripts_location, file)
+                #file.endswith('.conf') or file.endswith('.new'):
+                #fpath = os.path.join(self.scripts_location, file)
+                with open(fpath) as data:
+                    for line in data:
+                        if self.devname in line:
+                            return fpath
+        return self._get_next_available_config_file()
+    
+    def enable(self):
+        """ Enable this nic so it can be brought up automatically on boot """
+        return self.inet.enable()
+    
+    def disable(self):
+        """ Keep this nic from being brought up on boot """
+        return self.inet.disable()
+    
+    def check_status(self):
+        """ Check wether this Nic is being loaded @ startup """
+        return self.inet.check_status()
 
 class Inet(object):
-    """ Class used to represent an rc.inet entry in a vectorlinux system.
+    """ Class used to represend an rc.inet configuration file
+        in a vectorlinux system.
+        Arguments:
+            fpath    -   Absolute path to the file we are working with
+            nmethod  -   Networking method. Must be one of DHCP or STATIC
     
-    Arguments:
-        nic     -   Interface to be used for this inet.  Must be an instance
-                    of the class Nic.
-        nmethod -   Networking Method.  Defines how the NIC is configured.
-                    Must be one of STATIC or DHCP """
-    def __init__(self, nic=None, nmethod=DHCP):
-        assert isinstance(nic, Nic), 'Invalid nic argument.  Must use a Nic object'
-        assert nmethod in (DHCP, STATIC), 'Invalid networking method argument'
+    DO NOT USE THIS CLASS DIRECTLY.
+    Each Nic object gets an object of this class. It can be accessed
+    as the .inet attribute of the Nic object.
+    """
+    def __init__(self, fpath=None, nmethod=DHCP, nic=None):
+        self.fpath = fpath
+        self.nmethod = nmethod
         self.nic = nic
-        self.nmethod = nmethod
+        try:
+            self.config = self._read_config_data()
+        except:
+            self.config = self._generate_new_config_data()
     
-    def _generate_config_data(self):
-        """ Generate the inet script with the information provided. 
-        Returns the data as a list"""
+    def _run_command(self, cmd):
+        proc = get_popen(cmd)
+        stdout, stderr = proc.communicate()
+        code = proc.returncode
+        assert code == 0, stderr.strip()
+    
+    def enable(self):
+        """ Enable this inet by setting it's config file executable """
+        # First check to make sure the file exists
+        assert os.path.exists(self.fpath), \
+            "No such file %s in the system.  " % self.fpath + \
+            "  Has the configuration been saved yet?"
+        return self._run_command(['chmod', '+x', self.fpath])
+    
+    def disable(self):
+        """ Disable this inet by etting it's config file NOT executable """
+        assert os.path.exists(self.fpath), \
+            "No such file %s in the system."% self.fpath
+        return self._run_command(['chmod', '-x', self.fpath])
+    
+    def check_status(self):
+        """ Return True if inet is enabled.  False otherwise """
+        assert os.path.exists(self.fpath), \
+            "No such file %s in the system.  Cannot check its status"% self.fpath
+        return os.access(self.fpath, os.X_OK)
+
+    def _generate_new_config_data(self):
+        """ Generates new data for a config file.  Only
+        the config dictionary is generated here.  The header comments
+        and the script call are generated later. """
         if self.nmethod is DHCP:
-            dhcpval = "yes"
+            dhcpval = 'yes'
         else:
-            dhcpval = "no"
-        ret = [
-        "#!/bin/sh",
-        " This file is created by vasm",
-        " Avoid modifying it by hand",
-        "#",
-        "###################",
-        "#",
-        "## The settings",
-        "DEVICE='%s'"% self.nic.devname,
-        "DHCP='%s'"% dhcpval,
-        "IPADDR='%s'"%self.nic.ip_addr,
-        "NETMASK='%s'"% self.nic.netmask,
-        "GATEWAY=''",
-        "PROBE='no'",
-        " ",
-        "################## THE SCRIPT #########",
-        "# Source the standard networking script.",
-        ". /etc/rc.d/functions-network \"$@\""]
+            dhcpval = 'no'
+        ret = {
+            'DEVICE':self.nic.devname,
+            'DHCP': dhcpval,
+            'IPADDR': '127.0.0.1',
+            'NETMASK': '255.255.255.0',
+            'GATEWAY': '',
+            'PROBE':'no'}
         return ret
+    
+    def _generate_complete_config_file(self):
+        """ Returns a list with the data necessary to
+            write a complete new config file to the sysetem """
+        HEADER = [
+            "#!/bin/sh",
+            "# This file is created by vasm",
+            "# Avoid modifying it by hand.",
+            "# ",
+            "##################################",
+            "## The Settings. ",
+            ]
+        FOOTER = [
+            "# ",
+            "##################################",
+            "# ## The Script ## ",
+            "# Source the standard networking script.",
+            ". /etc/rc.d/functions-network \"$@\"","#",]
+        COMPLETE = []
+        # Add the header comments.
+        COMPLETE.extend(HEADER)
+        # Add the important data 
+        for comp in self.config:
+            _line = "%s='%s'"% (comp, self.config[comp])
+            COMPLETE.append(_line)
+        # Add the footer
+        COMPLETE.extend(FOOTER)
+        return COMPLETE
 
+    def _read_config_data(self):
+        """ Return a dictionary with the different options on
+            the config file. """
+        assert os.path.exists(self.fpath), \
+            "No such file %s.  %s represents a NEW unsaved rc.inet config file" % (
+                self.fpath, self.fpath)
+        ret = {}
+        with open(self.fpath) as data:
+            for line in data:
+                if self._line_sets_value(line):
+                    key, value = self._get_line_key_value(line)
+                    ret[key] = value
+        return ret
+    
+    def save_config(self):
+        """ Write the cofiguration to the filesystem.
+            Call this method after the differnt settings have
+            been modified """
+        # If the file doesn't exist, then we must write a new one
+        if not os.path.exists(self.fpath):
+            data = self._generate_complete_config_file()
+            f = open(self.fpath, 'w')
+            f.writelines('\n'.join(data))
+            f.close()
+            return
+        # The file already exists in the filesytem.  We must
+        # update it.
+        return self._do_write_changes()
+        
+    def _do_write_changes(self):
+        """ Only update the configuration data.  Leave the
+            file's comments alone """
+        ndata = []
+        with open(self.fpath) as data:
+            for line in data:
+                if self._line_sets_value(line):
+                    key, val = self._get_line_key_value(line)
+                    line = "%s='%s'"% (key, self.config[key])
+                ndata.append(line.strip())
+        # write the modified data to the filesystem
+        f = open(self.fpath, 'w')
+        f.writelines('\n'.join(ndata))
+        f.close()
+        return
+
+    def _line_sets_value(self, line):
+        """ Returns True if line sets a value.  False otherwise """
+        return "=" in line
+    
+    def _get_line_key_value(self, line):
+        """ Returns a tuple of (key, value) set by line """
+        key, val = line.split('=')
+        val = val.strip().replace("\'","").replace("\"","").strip()
+        return key, val
 
 class NetConfModel(object):
     """ Global networking model. """
-    def __init__(self, host_file='/etc/hostname',
+    def __init__(self, host_file='/etc/HOSTNAME',
         dns_file='/etc/resolve.conf',
-        hostname='vector.linux.vnet', nic_defs='/proc/net/dev'):
-        self.nic_defs = nic_defs
+        hostname=None):
         self.host_file = host_file
         self.dns_file = dns_file
         self.hostname = hostname
+    
+    def set_hostname(self):
+        """ Set the machine hostname to the value specified in self.hostname"""
+        assert self.hostname not in ('',None), \
+            "Empty hostname is not accepted.  Please set the .hostname attribute first"
+        f = open(self.host_file, 'w')
+        f.writelines("\n".join([self.hostname,]))
+        f.close()
+    
+    def get_hostname(self):
+        """ read the hostname from the file """
+        f = open(self.host_file, 'r')
+        data = f.readline()
+        f.close
+        return data.strip()
 
     def listNetworkingDaemons(self):
         """ Returns a list of known networking daemons available on 
         DAEMONS = {'networkmanager':
             {'path': '/usr/sbin/NetworkManager',
                 'initscript' : '/etc/rc.d/rc.networkmanager',
-                'service' : None},
+                'service' : None,
+                'sessionscript' : '/etc/xdg/autostart/nm-applet.desktop'},
             'wicd' : {
                 'path': '/usr/sbin/wicd',
                 'initscript' : '/etc/rc.d/rc.wicd',
-                'service': None}}
+                'service': None,
+                'sessionscript' : '/etc/xdg/autostart/wicd-tray.desktop'}}
         for netapp in DAEMONS:
             if os.path.exists(DAEMONS[netapp]['path']):
                 ndaemon = NDaemon(
                     name = netapp,
                     bin_path = DAEMONS[netapp]['path'],
                     init_script = DAEMONS[netapp]['initscript'],
-                    service_script = DAEMONS[netapp]['service'])
+                    service_script = DAEMONS[netapp]['service'],
+                    sessionscript = DAEMONS[netapp]['sessionscript'])
                 ret.append(ndaemon)
         return ret
     
+    def hasDaemon(self, daemon_name):
+        """ Check if the system has daemon_name available """
+        options = self.listNetworkingDaemons()
+        for item in options:
+            if item.name == daemon:
+                return True
+        return False
+    
     def useDaemon(self, daemon):
         """ Configure the system to use the specified networking daemon """
+        assert self.hasDaemon(daemon) is True, \
+            "The %s daemon is not available on this system."% daemon
+        # FIXME: Do we want to disable the manual setup? ie, run nic.disable()
+        #       on all of the system's adaptors?
         options = self.lietNetworkingDaemons()
         valid = False
         for bin in options:
         if not valid:
             raise MissingDaemonException(
             "Specified daemon is not available on this system.")
+
+    def useManualSetup(self, nic):
+        """ Do not use daemon for networking, but rather automatically 
+        bring up an interface as configured. """
+        # Turn off all daemons
+        for program in self.listNetworkingDaemons():
+            program.disable()
+        # Disable all interfaces except for the one specified
+        for iface in self.listNetworkAddapters():
+            if iface != nic:
+                iface.disable()        
+        return
     
+    def useNoNetworking(self):
+        """ Disable all kinds of automatic networking configuration """
+        # Disable all kinds of automatic network configuration.
+        # Disable daemons
+        for program in self.listNetworkingDaemons():
+            program.disable()
+        # Disable adaptors
+        for _nic in self.listNetworkAddapters():
+            _nic.disable()
+        return
+
     def listNetworkAdapters(self):
         """ Return a list of Nic objects """
         ret = []
             broadcast = nicdata[AF_INET][0]['broadcast']
             nmask = nicdata[AF_INET][0]['netmask']
             macaddr = nicdata[AF_LINK][0]['addr']
+            # Create the Nic objet.
+            # The new Nic object will get it's own Inet object.
             _NIC = Nic(devname = nic,
                 netmask = nmask,
                 bcast = broadcast,