Commits

Rodrigo Bistolfi committed 6c2c55e Merge with conflicts

Implemented advanced install method

Merge branch 'advanced'

Conflicts:
vinstall/backend/media.py
vinstall/backend/partitioning.py
vinstall/controller/automatic.py

Comments (0)

Files changed (18)

vinstall/backend/bootloader.py

 
 class Lilo(object):
     """LiLo bootloader
-    
+
     """
-    def __init__(self, target, default_os, timeout=0, 
-            vga_mode="nornal", include_running_os=False):
-        
+    def __init__(self, target, default_os=None, timeout=3,
+            vga_mode="788"):
+
         self.target = target
         self.default_os = default_os
         self.vga_mode = vga_mode
-        self.include_running_os = include_running_os
         self.operating_systems = []
-        self._timeout = timeout
-        self.buffer = StringIO()
+        self.timeout = None
+        self.set_timeout(timeout)
+        self.buffer = []
         self.lilo_root = utils.get_mounted("/")
         tamu = "/boot/tamu"
         if not os.path.exists(tamu):
             os.mkdir(tamu)
-    
+
     def add_os(self, system):
         if system.type == "linux":
             self.add_unix_os(system)
 
     def add_unix_os(self, system):
         """Add an operating system to the boot menu
-        
+
         """
         if system in self.operating_systems:
             raise RuntimeError("OS already added")
         if system.root != self.lilo_root:
-            
+
             # mount os root partition if needed
             umount = False
             partition = media.Partition(system.root)
                     os.mkdir(mountpoint)
                 partition.mount(mountpoint)
                 umount = True
-            
+
             # copy kernel
-            kernel_from = os.path.join(partition.mountpoint,
+            kernel_from = os.path.join(partition.mountpoint, "boot",
                     system.kernel_name)
             kernel_dest = os.path.join("/boot/tamu", system.kernel)
             shutil.copy2(kernel_from, kernel_dest)
-            
+
             # copy initrd if needed
             if system.initrd:
                 initrd_from = os.path.join(partition.mountpoint,
                         system.initrd)
                 initrd_dest = os.path.join("/boot/tamu",
                         system.initrd)
-                shutil.copy2(initrd_from, initrd_dest)
-            
+                if os.path.exists(initrd_from) and \
+                        not os.path.exists(initrd_dest):
+                    shutil.copy2(initrd_from, initrd_dest)
+
             # umount if needed
             if umount:
                 partition.umount()
 
             # write config lines
             tab = " " * 4
-            self.buffer.write("# -- %s on %s --\n" % (system.label,
+            self.buffer.append("# -- %s on %s --" % (system.label,
                 system.root))
-            self.buffer.write("image = %s\n" % system.kernel)
-            self.buffer.write("%s label = %s\n" % (tab, system.label))
+            self.buffer.append("image = %s" % system.kernel)
+            self.buffer.append("%s label = %s" % (tab, system.label))
             if system.initrd:
-                self.buffer.write("%s initrd = %s\n" % (tab,
+                self.buffer.append("%s initrd = %s" % (tab,
                     system.initrd))
             if system.appendline:
                 if "splash" not in system.appendline:
-                    self.buffer.write('%s append = "%s splash=silent"\n') % (tab,
-                            system.appendline)
+                    self.buffer.append('%s append = "%s splash=silent"' %
+                            (tab, system.appendline))
                 else:
-                    self.buffer.write('s append = "%s"\n' % (tab,
+                    self.buffer.append('s append = "%s"' % (tab,
                             system.appendline))
-            self.buffer.write("%s read-only\n" % tab)
-            self.buffer.write("# -- \n\n")
+            self.buffer.append("%s read-only" % tab)
+            self.buffer.append("# --")
 
             self.operating_systems.append(system)
 
     def add_ms_os(self, system):
         """Add MS os to boot menu
-        
+
         """
 
     def set_default_os(self, system):
         """Set the default OS
-        
+
         """
         self.default_os = system
 
     def set_timeout(self, seconds):
         """Wait for <timeout> seconds before booting the default OS
-        
+
         """
-        self._timeout = int(seconds) * 10
+        self.timeout = int(seconds) * 10
 
     def set_vga_mode(self, resolution, quality):
         """Set the VGA mode
-        
+
         """
         values = {
                 "640x480" : {
                     "high" : "788"},
                 "1024x768" : {
                     "low" : "773",
-                    "med" : "790", 
+                    "med" : "790",
                     "high" : "791"},
                 "1280x1024": {
                     "high":"794",
 
     def backup_config(self):
         """Backup existing lilo.conf
-        
+
         """
 
-    def write_config(self):
-        """Write lilo.conf. 
-        
+    def write_config(self, config_path="/etc/lilo.conf"):
+        """Write lilo.conf.
+
         """
         newline = "\n"
-        config_path = "/etc/lilo.conf"
         header = (
             "# LILO configuration file.",
             "# ",
             "bitmap = /boot/bitmap/boot.bmp",
             "vga = %s" % self.vga_mode,
             "# OS list")
-        
+
         with open(config_path, "w") as liloconf:
             for i in header:
                 liloconf.write(i + newline)
             for line in self.buffer:
-                liloconf.write(line)
-               
+                liloconf.write(line + newline)
+
     def install(self):
         """Install the bootloader
-        
+
         """
         return subprocess.check_call(["/sbin/lilo"])
 
+    def test_install(self, config_file, bootdev):
+        """Install routine for testing
+
+        """
+        cmd = "/sbin/lilo -b %s -C %s" % (bootdev, config_file)
+        return subprocess.check_call(cmd.split())
+
 
 class Grub2(object):
     """Grub2 bootloader
-    
+
     """
-    def __init__(self, target, timeout, vga_size, vga_quality):
+    def __init__(self, target):
         self.config_path = "/etc/default/grub"
         self.type = GRUB2
-        self.timeout = timeout
+        self.timeout = 3
         self.target = target
         self.vga_mode = None
-    
+        self.set_vga_mode()
+
     def set_vga_mode(self, resolution="800x600"):
         modes = {
                 "640x480": "640x480x16",
                 "1280x1024": "1280x1024x16"}
         self.vga_mode = modes.get(resolution, "800x600")
 
-    def write_config(self, filepath="/etc/default/grub"):
+    def backup_config(self):
+        pass
+
+    def write_config(self, config_path="/etc/default/grub"):
         """The grub2 config file is automatically generated, so we use
-        this method to save the values to /etc/default/grub which is 
-        parsed while the config file is generated 
-        
+        this method to save the values to /etc/default/grub which is
+        parsed while the config file is generated
+
         """
 
         # set the correct vga_mode value
                  "# END OF /etc/default/grub",""]
 
         # save the new file
-        with open(filepath, "w") as f:
+        with open(config_path, "w") as f:
             f.writelines("\n".join(ndata))
-        
+
         # after these are set, we just need to run grub-mkconfig
         subprocess.check_call(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"])
 
     def install(self):
         """Install grub to the target
-        
+
         """
+        subprocess.check_call(["grub-mkdevicemap", "--no-floppy"])
         subprocess.check_call(["grub-install", "--no-floppy", self.target])
 
+    def test_install(self, config_file, bootdev):
+        subprocess.check_call(["grub-mkdevicemap", "--no-floppy"])
+        subprocess.check_call(["grub-install", "--no-floppy", bootdev])
+
 
 class OperatingSystem(object):
     """An OS to be added to a bootloader
-    
+
     """
     type = None         # str indicating OS type
     kernel = None       # Kernel image
     root = None         # path to OS root
-    appendline = None   # append line for lilo.conf 
+    appendline = None   # append line for lilo.conf
 
     @property
     def kernel_name(self):
     def all(cls, include_running_os=False):
         """Return one OperatingSystem instance for each OS
         installed
-        
+
         """
         os_prober = subprocess.check_output(["os-prober", "&>/dev/null"])
         found_os = os_prober.split("\n")
             running_os.type = "linux"
             if os.path.exists('/boot/initrd'):
                 running_os.initrd = '/boot/initrd'
-            running_os.root = None #XXX
+            running_os.root = utils.get_mounted("/")
             yield running_os
 
         for i in found_os:
             if os_type.lower() == "linux":
                 operating_system.type = "linux"
                 bootinfo = get_linux_boot_info(root)
-                if bootinfo is None: 
+                if bootinfo is None:
                     continue
                 operating_system.root = bootinfo["root"] or root
                 operating_system.kernel = bootinfo["kernel"]
 
 class MasterBootRecord(object):
     """The MBR of a device
-    
+
     """
     def __init__(self, device):
         self.device = device # path to the device as in /dev/sda
 
     def read(self, bytes=2):
         """Read n bytes
-        
+
         """
         with open(self.device) as f:
             data = f.read(bytes)
 
     def bootloader(self):
         """Return the bootloader installed in the MBR if any
-        
+
         """
         bytes = self.read()
         if bytes == "\xEB\x63":
 
 
 def get_linux_boot_info(root_partition):
-    """Use linux-boot-prober to find out how to boot this OS 
+    """Use linux-boot-prober to find out how to boot this OS
 
     /dev/sdb1:/dev/sdb1::/boot/vmlinuz26:/boot/kernel26.img:root=/dev/sdb1
-    
+
     """
     proc = subprocess.check_output(['linux-boot-prober', root_partition])
     hits = proc.split("\n")
     return dict(root=root, kernel=kernel, initrd=initrd, append=append)
 
 
-class FakeDevice(unittest.TestCase):
+class FakeDeviceTestCase(unittest.TestCase):
+
     def setUp(self):
         (fd, self.path) = tempfile.mkstemp(prefix="fake-device-")
         f = os.fdopen(fd)
         os.unlink(self.path)
 
 
+class LiloTestCase(FakeDeviceTestCase):
+
+    def test_lilo(self):
+        import os
+        if not os.path.exists('/bin/lilo'):
+            print "LiLO not found.  Skipping test"
+            return True
+        lilo = Lilo(self.path)
+        for ops in OperatingSystem.all(include_running_os=True):
+            lilo.add_os(ops)
+        lilo.write_config(config_path="/tmp/lilo-test")
+        lilo.test_install("/tmp/lilo-test", self.path)
+        mbr = MasterBootRecord(self.path)
+        self.assertEqual(mbr.bootloader(), LILO)
+
+
+class Grub2TestCase(FakeDeviceTestCase):
+
+    def test_grub2(self):
+        return True
+        grub2 = Grub2(self.path)
+        grub2.write_config(config_path="/tmp/grub2-test")
+        grub2.test_install("/tmp/grub2-test", self.path)
+        mbr = MasterBootRecord(self.path)
+        self.assertEqual(mbr.bootloader(), GRUB2)
+
+
 if __name__ == "__main__":
     unittest.main()

vinstall/backend/fstab.py

         if self.filesystem in ("swap","none","linux-swap","linux-swap(v1)"):
             return opts["swap"]
         else:
-            return opts.get(self.filesystem, "defauls 0 0")
+            return opts.get(self.filesystem, "defaults 0 0")
 
     def find_uuid(self):
         """ Find the uuid for the specified device

vinstall/backend/installmedia.py

                 pass
             else:
                 raise e
-        partitions = media.Partition.all()
         cdroms = ( media.Partition(i) for i in media.list_cdroms() )
         partitions = media.Partition.all()
         #XXX refactor for DRY
             if needs_umount:
                 cdrom.umount()
         for partition in partitions:
-            needs_umount = False
             if not partition.is_mounted():
-                partition.mount(dev_mountpoint)
+                partition.mount(partition.mountpoint)
                 needs_umount = True
             if is_install_media(partition.mountpoint):
                 config = parse_config(partition.mountpoint)
     """
     conf = "veclinux/VINSTALL.INI"
     config = ConfigParser.RawConfigParser()
+    config.optionxform = str
     config.read(os.path.join(mnt,conf))
     return config
 
 
+def make_mountpoint(device):
+    """Create mountpoint for device
+
+    """
+    mountpoint = device.replace("/dev", "/mnt")
+    if not os.path.exists(mountpoint):
+        os.mkdir(mountpoint)
+
+
 def is_install_media(mnt):
     """ Checks for VINSTALL.INI the install config file added in 6.0
 

vinstall/backend/media.py

 """
 
 __author__ = "rbistolfi"
-# 2012-09-10 Modified by M0E-lnx to use the pyparted API instead of reparted
 
 
 import re
 import os
 import subprocess
 from utils import mount, umount, is_mounted, format_partition
-import partitioning
 import parted
-#from parted import device as pdevice
-#from parted import disk as pdisk
+from _ped import DiskLabelException
+
 
-# I think we should have a config.py and import settings from there
 DEV_DIR = '/dev'
 CD_INFO = '/proc/sys/dev/cdrom/info'
 PARTITIONS = '/proc/partitions'
             filesystem = self.query_filesystem()
         else:
             filesystem = "Not formated"
-        return '%s (%s)' % (self.device_path, filesystem)
+        return '%s (%s %s)' % (self.device_path, self.size("GB"), filesystem)
 
     def __repr__(self):
         return '<Partition object: %s>' % self.device_path
 
     def __init__(self, device_path):
         """Initializes a Media object fro the device path."""
-
-        self.mountpoint = None
+	mntpoint = device_path.split("/")[2] 
+	mntpoint =  "/mnt/" + mntpoint         
+        if not os.path.exists(mntpoint):
+            os.mkdir(mntpoint)
+	self.mountpoint = mntpoint 
         self.device_path = device_path
         if self.is_mounted():
            for line in open("/etc/mtab"):
         """Returns True if the media is mounted, False otherwise."""
 
         return is_mounted(self.device_path)
+    
+    def size(self, unit="GB"):
+        """Return the partition size in the specified unit.  Defaults to GB"""
+        su = round(self._parted_partition.getLength(unit), 1)
+        return "%s %s"% (su, unit)
+
 
     def query_filesystem(self):
         """Retrieve the current filesystem type from the system."""
-
-        process = subprocess.Popen(['blkid', self.device_path],
-                stdout=subprocess.PIPE)
-        data = process.stdout.read()
-        process.stdout.close()
-        try:
-            return re.search(r'TYPE="(\w+)"', data).group(1)
-        except AttributeError:
-            #blkid returned nothing
-            return None
+        pfs = self._parted_partition.fileSystem.type
+        if "swap" in pfs:
+            pfs = "swap"
+        return pfs
 
     def format(self, filesystem):
         """Create a filesystem in this partition.
         """
         format_partition(self.device_path, filesystem)
 
+    def path(self):
+        """Return the device path (only for interface compatibility with
+        Disk)
+
+        """
+        return self.device_path
+
     @classmethod
     def all(cls):
         """Return all the partitions in the system
 
         """
-        disks = [ i for i in Disk.all() if i._has_partition_table ]
+        disks = [ i for i in Disk.all() if i._disk is not None ]
         partitions = []
         for dsk in disks:
             partitions.extend(dsk._disk.partitions)
-        
+
         for part in partitions:
             if part.type in (parted.PARTITION_NORMAL,
                              parted.PARTITION_LOGICAL,):
-                yield cls(part.path)
+                p = cls(part.path)
+                p._parted_partition = part # We could use this for other operations.
+                yield p
 
 
 class Disk(object):
 
         self._device = None
         self._disk = None
-        self._has_partition_table = False
 
     def __repr__(self):
 
 
     def __str__(self):
 
-        return "%s - %s GB (%s)" % (self.model(),  self.size(), self.path())
+        return "%s - %s GB (%s)" % (self.model(), round(self.size(), 2), self.path())
 
     def model(self):
         """Return the string describing the device model"""
         return self._device.model
 
-    def free_space(self, units="MiB"):
+    def free_space(self, units="MB"):
         """Return the ammount of free space available in units specified"""
         raise RuntimeError("Deprecated method")
         #return self._disk.usable_free_space.to(units)
 
-    def _get_size_from_hw(self, unit="GiB"):
+    def _get_size_from_hw(self, unit="GB"):
         """ Returns disk size, by default in GB, unit can be GB or MB
         This method is used internally when the disk is not initialized.
-        """
-        pow = 2 if unit == "MiB" else 3
+
+	"""
+        pow = 2 if unit == "MB" else 3
         cylinders,  heads,  sectors = self._device.hardwareGeometry
         sector_size = self._device.physicalSectorSize #self._device.sector_size
         size = cylinders * heads * sectors * sector_size /1000 ** pow
 
     @property
     def has_partition_table(self):
-        """Return True if the disk has a partition table.  False otherwise"""
-        # test if the device has a partition table yet
-        return self._has_partition_table
-        try:
-            pdisk = parted.Disk(self._device)
-            del pdisk
-            return True
-        except:
-            return False
-
-    def size(self, unit="GiB"):
+        """Return True if the disk has a partition table.  False otherwise
+
+	"""
+        return self._disk is not None
+
+    def size(self, unit="GB"):
         """ Returns disk size, by default in GB, unit can be GB or MB
 
         """
         return self._device.getLength(unit)
+        #return self._device.getSize(unit)
 
     def path(self):
         """Return the node path for this device"""
             raise RuntimeError("No hard drives found")
         for dev in devices:
             disk = cls()
-            try:
-                disk._disk = pdisk.Disk(dev)
-                disk._has_partition_table = True
-            except:
-                #disk._disk = parted.freshDisk(dev, 'msdos') # Create the object with an empty partition table in it ??
-                # FIXME: ^^
-                disk._has_partition_table = False                
             disk._device = dev
+            try:
+                disk._disk = parted.Disk(dev)
+            except DiskLabelException, e:
+                print "%s Has no partion table. Cannot read partitions" % disk
             yield disk

vinstall/backend/mountpoints.py

+# -*- coding: utf8 -*-
+
+
+"""Mountpoints module
+
+"""
+
+
+from vinstall.backend.media import Partition
+
+class MountPoints(object):
+    
+    DEFAULT_ROOT_FS = "ext3" # Default os to be used for /, unless changed by the user.
+    MINIMUM_ROOT_SIZE = 4 # Size in GB
+
+    def __init__(self):
+
+        self.partitions = set(Partition.all())
+        self.filesystems = set(["ext2", "ext3", "ext4", "reiserfs", "xfs",
+            "jfs", "swap"])
+        self.mountpoints = set(["/", "home", "boot", "var", "tmp", "opt",
+            "usr", "swap"])
+        self.mapping = dict((i, [None, None]) for i in self.partitions)
+        
+        self.default_filesystem = self.DEFAULT_ROOT_FS
+        #XX: ^^ For compatibility purposes only.
+
+    def set_mount(self, partition, mountpoint):
+        """Declare a mountpoint for a partition
+
+        """
+        self.mapping[partition][0] = mountpoint
+
+    def clear_mount(self, partition):
+        """Declare that a partition shouldnt be mounted anymore
+
+        """
+        self.mapping[partition][0] = None
+
+    def set_filesystem(self, partition, fs):
+        """Declare that a partition should be formatted with fs
+
+        """
+        self.mapping[partition][1] = fs
+
+    def clear_filesystem(self, partition):
+        """Declare that a partition sholdnt be formatted
+
+        """
+        self.mapping[partition][1] = None
+
+    def available_partitions(self):
+        """Return available partitions
+
+        """
+        return self.partitions - set(self.mapping.keys())
+
+    def available_mountpoints(self):
+        """Return available mountpoints
+
+        """
+        return self.mountpoints - set(i[0] for i in self.mapping.values())

vinstall/backend/partitioning.py

 
 import utils
 import parted
+import unittest, tempfile, os
+from vinstall.backend import media
 
 __author__ = "Moises Henriquez"
 __email__ = "moc.liamg@xnl.e0m"[::-1]
                 disk - a media.Disk object
 
         """
-        #self.disk = disk._disk
         self.disk = disk
 
+    def has_partition_table(self):
+        """Find if there is a partition table. PTs start at position 0x1BE and
+        contains 4 entries of 16 bytes, one for each partition
+        
+        """
+        with open(self.disk._device.path) as f:
+            f.seek(0x1BE)
+            data = f.read(16)
+            if data == "\x00" * 16:
+                return False
+            else:
+                return True
+
     def create_partition_table(self, table_type="msdos"):
         """ Create the partition table on the disk.
         This is only necessary when using a fresh disk that has
         never been partitioned before.
 
         """
-        assert self.disk._disk is None, "Partition table already exists?"
-#        assert self.disk._disk._has_partition_table is False, "Partition table already exists?"
-#        assert not hasattr(self.disk, "_disk"), ".disk attribute already here.  Do we have a partition table already?"
-#        assert self.disk._disk is not None, "We already have .disk attribute.  Do we have a partition table already?"
-#        assert self.disk.type is None, "Disk already has a partition table"
         self.disk._disk = parted.freshDisk(self.disk._device, "msdos")
-        self.disk._disk._has_partition_table = True
-
+        self.disk._disk.has_partition_table = True
 
     def delete_all_partitions(self):
         """ Delete all partitions from drive
 
         """
-        assert self.disk._has_partition_table is True, "No partition table on disk."
-        self.disk.deleteAllPartitions()
+        #self.disk._disk.deleteAllPartitions()
+        self.disk._disk = parted.freshDisk(self.disk._device, "msdos")
+        self.disk._disk.has_partition_table = True
 
-    def add_partition(self,  size=0, units='MiB'):
+    def partitions_number(self):
+        """Return the number of partitions in disk
+        
+        """
+        return len(self.disk._disk.partitions)
+
+    def add_partition(self,  size=0, units='MB'):
         """Add a partition to the disk.  
         Args:  size = partition size
                units = partition units (MB, GB, MiB, GiB), (defaults to MiB)
 
         """
-        assert self.disk.has_partition_table is False, "No partition table on disk.  Cannot add partitions"
         mypsize = parted.sizeToSectors(size, units, self.disk._device.sectorSize)
         # first partition starts at sector 1
         # find out where the free space is
         myconst = parted.Constraint(device = self.disk._device)
         for i in self.disk._disk.getFreeSpaceRegions():
             if i > mypsize:  # partition fits here
-                start_marker = i.start
-        print "Creating new partition @ block %s and extending to %s"% (start_marker, mypsize)
+                if not self.partitions_number():
+                    start_marker = max(i.start, 2048)
+                else:
+                    start_marker = i.start
+                break
+        else:
+            raise RuntimeError("There is no free space for partition")
+
         mygeom = parted.Geometry(device = self.disk._device,
                                  start = start_marker,
                                  length = mypsize)
         self.disk._disk.addPartition(partition = mypartition,
                                      constraint = myconst)
 
-        
-
-        return
-        # OLD CODE ----
-        if self.disk.type_name is None:
-            self.create_partition_table()
-
-        psize = reparted.Size(size,  units)
-        partition = reparted.Partition(self.disk,  size=psize)
-        self.disk.add_partition(partition)
-
     def write_changes(self):
         """Finalize changes to the disk.  This needs to be called
         after creating partitions or deleting partitions to make sure
         self.disk._disk.commit()
 
 
+class DiskPartitionerTestCase(unittest.TestCase):
+
+    def setUp(self):
+        (fd, self.path) = tempfile.mkstemp(prefix="fake-device-")
+        f = os.fdopen(fd)
+        f.seek(1400000)
+        os.write(fd, "0")
+
+    def tearDown(self):
+        os.unlink(self.path)
+
+    def test_create_partition_table(self):
+        device = parted.Device(self.path)
+        disk = media.Disk()
+        disk._device = device
+        p = DiskPartitioner(disk)
+        p.create_partition_table()
+        p.add_partition(size=0.1)
+        p.write_changes()
+        self.assertTrue(p.has_partition_table())
+
+    def test_add_partition(self):
+        device = parted.Device(self.path)
+        disk = media.Disk()
+        disk._device = device
+        p = DiskPartitioner(disk)
+        p.create_partition_table()
+        p.add_partition(size=0.1)
+        p.write_changes()
+        self.assertEqual(len(disk._disk.partitions), 1)
+
+    def test_has_partition_table(self):
+        device = parted.Device(self.path)
+        disk = media.Disk()
+        disk._device = device
+        p = DiskPartitioner(disk)
+        self.assertFalse(p.has_partition_table())
+        p.create_partition_table()
+        p.add_partition(size=0.1)
+        p.write_changes()
+        self.assertTrue(p.has_partition_table())
+    
+    def test_delete_all_partitions(self):
+        device = parted.Device(self.path)
+        disk = media.Disk()
+        disk._device = device
+        p = DiskPartitioner(disk)
+        p.create_partition_table()
+        p.add_partition(size=0.05)
+        p.add_partition(size=0.05)
+        p.write_changes()
+        self.assertEqual(len(disk._disk.partitions), 2)
+        p.delete_all_partitions()
+        p.write_changes()
+        self.assertEqual(len(disk._disk.partitions), 0)
+
+
+if __name__ == "__main__":
+    unittest.main()

vinstall/backend/users.py

 __author__ = "Moises Henriquez"
 __author_email__ = "moc.liamg@xnl.E0M"[::-1]
 
-"""Api for managing and creating user accounts on VectorLinux"""
+
+"""Api for managing and creating user accounts on VectorLinux
+
+"""
+
+
 import pwd
 import grp
 import crypt
 import subprocess as sp
 import unittest
 from utils import Chroot
-#from vinstall.backend.utils import Chroot
 
 
 class User(object):
         self.fullname = None
 
     def _setup_home(self):
-        """Setup the default home directory for this user"""
-        # This should not be needed, but just in case someone
-        # decides to call this directly.
-        assert self._system_data is not None, "User account not in system"
-        assert self.login is not None, "Does this account exist yet?"
+        """Setup the default home directory for this user
+        
+        """
+        #assert self._system_data is not None, "User account not in system"
+        #assert self.login is not None, "Does this account exist yet?"
         SKELPATH = "/etc/skel"
         TARGETDIR= self.home
 
         topdir, dirnames, files = os.walk(SKELPATH).next()
         
         # Copy the skel files to the home dir
-        # FIXME:  Does useradd do this by default on VL?
+        # XXX  Does useradd do this by default on VL?
         for item in os.listdir(SKELPATH):
             fcopy = ["/bin/cp", "-aru", os.path.join(SKELPATH, item), 
                    os.path.join(TARGETDIR, item)]
             for fun in (fcopy, fchown, fchgrp):
                 sp.check_call(fun)
 
-
-
         # Set permissions to the items in home directory.
         cmd = ["/bin/chmod", "0700", TARGETDIR]
         sp.check_call(cmd)
 
     def create(self):
         """Create the user account on the system"""
-        assert self._system_data is None, "User account already exists"
-        assert self.password is not None, "Password attribute has not been set"
-        assert self.login is not None, "Login attribute has not been set"
+        #assert self._system_data is None, "User account already exists"
+        #assert self.password is not None, "Password attribute has not been set"
+        #assert self.login is not None, "Login attribute has not been set"
         epass = self._encrypt_password(self.password)
 
         # Add the group
 
     def change_password(self, newpass):
         """Change a users password"""
-        assert len(newpass) > 4, "Invalid password"
-        assert self._system_data is not None, "User does not exist in the system."
+        #assert len(newpass) > 4, "Invalid password"
+        #assert self._system_data is not None, "User does not exist in the system."
         # Not needed for the installer.  Only for VASM
         epass = self._encrypt_password(newpass)
-        assert epass != newpass, "Password cannot be encrypted correctly"
+        #assert epass != newpass, "Password cannot be encrypted correctly"
         cmd = ["/usr/sbin/usermod", "-p", epass, self.login]
         return sp.check_call(cmd)
 
         """delete this user account"""
         # if self._data is None, this user account does not yet exist.
 
-        assert self._system_data is not None, "User not in system"
+        #assert self._system_data is not None, "User not in system"
         cmd = [ "/usr/sbin/userdel", "-r", self.login]
         return sp.check_call(cmd)
 
     def set_initial_group(self, grouname="users"):
         """Set the initial group for this account.  This is normally 
         'users' for human user accounts"""
-        assert self._system_data is not None, "User does not exist in the system."
+        #assert self._system_data is not None, "User does not exist in the system."
         cmd = [ "/usr/sbin/usermod", "-g", groupname, self.login]
         return sp.check_call(cmd)
 
     def set_supplementary_groups(self, grouplist=[]):
         """Set the supplementary group memberships for this user"""
-        assert self._system_data is not None, "User does not exist in the system."
-        assert isinstance(grouplist, list), "grouplist argument must be a list"
+        #assert self._system_data is not None, "User does not exist in the system."
+        #assert isinstance(grouplist, list), "grouplist argument must be a list"
         cmd = [ "/usr/sbin/usermod", "-G", ",".join(grouplist), self.login ]
         return sp.check_call(cmd)
 
     def add_to_group(self, group):
         """Add this user account to the specified group """
-        assert self._system_data is not None, "User not in system yet"
-        assert isinstance(group, str), "Group argument must be a string"
+        #assert self._system_data is not None, "User not in system yet"
+        #assert isinstance(group, str), "Group argument must be a string"
         allgroups = [ g.gr_name for g in grp.getgrall() ]
-        assert group in allgroups, "Group %s does not exist in the system."% group
+        #assert group in allgroups, "Group %s does not exist in the system."% group
         
         cmd = [ "/usr/sbin/usermod", "-a", group, self.login ]
         return sp.check_call(cmd)
     @property
     def _system_data(self):
         """Return the system data related to this account"""
-        assert self.login is not None, "Login property must be set first"
+        #assert self.login is not None, "Login property must be set first"
         ret = [ u for u in pwd.getpwall() if u.pw_name == self.login ]
         if ret:
             return ret[0]

vinstall/backend/utils.py

     """
     if 'swap' in filesystem:
         command = 'mkswap %s'% path
+    elif 'reiser' in filesystem:
+        command = 'mkfs.%s -f -f %s' % (filesystem, path)
     else:
         command = 'mkfs.%s -F %s' % (filesystem, path)
     subprocess.call(command.split())
 
+
 def activate_swap(path=None):
     if path is None:
         #Activate all swap space
 
     """
     command = 'mount -t %s %s %s' % (filesystem, src, mountpoint)
-    print command
     subprocess.call(command.split())
     return mountpoint
 
             return False
 
 
+def get_mounted(mountpoint):
+    """Return device for a mountpoint
+    
+    """
+    for line in open("/etc/mtab"):
+        device, mpoint, _ = line.split(" ", 2)
+        if mountpoint == mpoint:
+            return device
+
+
 def supported_filesystems():
     """Returns a list of filesystems supported by the system."""
 
     """
     def __init__(self, new_root, chdir=True):
         self.new_root = new_root
-        self.chdir = True
+        self.chdir = chdir
 
     def __enter__(self):
         """Save current root for restoring later

vinstall/controller/advanced.py

         if index == 1:
             if self.is_display_set():
                 command = "/usr/sbin/gparted".split()
-                subprocess.Popen(command)
+                subprocess.call(command)
 
     def is_display_set(self):
         """Return True if graphical program should be used

vinstall/controller/automatic.py

 
 
 from vinstall.core import Render, Controller, model
-from vinstall.backend import media,  utils,  partitioning
-from vinstall.backend import fstab
-from vinstall.backend import users
-from vinstall.backend import service
-from vinstall.backend import bootloader
+from vinstall.backend import (media,  utils,  partitioning, fstab, users,
+        service, bootloader)
 import vinstall.controller.intro as intro
-import os, subprocess
-import glob
-import shutil
+import os, subprocess, glob, shutil
+
 
 class AutomaticMode(Controller):
     """Automatic install mode. We will look for devices suitable for
         """Jump to the basic Setup
 
         """
-        return Setup
+        return UserSetup
 
     # utility methods
 
         """Do automatic partitioning in disk
 
         """
-        print "Partitioning", disk
-
-        if not self._check_continue():
-            return
-
         available_ram = int(utils.get_mem_size())
 
         if available_ram <= 512:
         else:
             swap_size = int(available_ram / 2)
 
-        disk_size = disk.size(unit="MiB")
+        disk_size = int(disk.size(unit="MB"))
         partitioner = partitioning.DiskPartitioner(disk)
         if not disk.has_partition_table:
-            print "Creating partition table"
             partitioner.create_partition_table()
         else:
             partitioner.delete_all_partitions()
-        
+
         root_size = disk_size - swap_size
-        partitioner.add_partition(size=root_size, units='MiB')
-        # Subtract 1MB from the swap size to let pyparted align the partitions right.
-        partitioner.add_partition(size=swap_size-1, units='MiB')
+        partitioner.add_partition(size=root_size, units='MB')
+        partitioner.add_partition(size=swap_size-1, units='MB')
         partitioner.write_changes()
 
 
         """Format partitions in disk with the default filesystem
 
         """
-        print "Formatting partitions in", disk
-
-        if not self._check_continue():
-            return
-
         proot = "%s%s" % (disk.path(), 1)
         pswap = "%s%s" % (disk.path(), 2)
-        # make sure our partitions are not mounted before we attempt to
-        # format them.  Especially the target's /
         utils.umount('/mnt/TARGET')
         utils.format_partition(proot, self.ROOT_FS)
         utils.format_partition(pswap, 'swap')
             os.mkdir(mountpoint)
         install_media = self.config["install_media"]
         if install_media.is_iso():
+            if not os.path.exists(install_media.path):
+                print "mount container here"
+		mntpoint = install_media.path.rsplit("/", 1)[0]
+		dev = mntpoint.replace("/mnt", "/dev")
+            	utils.mount(dev, mntpoint)
             utils.mountiso(install_media.path, mountpoint)
         else:
             utils.mount(install_media.path, mountpoint, filesystem="auto")
         In automatic mode this should be every package in the iso.
 
         """
-        print "Looking for packages"
-        excluded =("vlconfig2", "vlsetup", "aaa_base")
+        excluded =("vlconfig2", "vlsetup", "aaa_base", "VL_Theme")
         for root, dirs, files in os.walk("/mnt/SOURCE/packages"):
             for file in files:
                 fname, fext = os.path.splitext(file)
         """Install package in the disk root partition
 
         """
-        print "Installing %s in %s" % (package, disk)
         command = "/usr/sbin/installpkg %s --root=%s" % (package, "/mnt/TARGET")
-        if self._check_continue():
-            subprocess.check_call(command.split())
+        subprocess.check_call(command.split())
 
     def preinstall(self):
         """Run before installing packages, used for installing required
         packages first.
 
         """
-        if not self._check_continue():
-            return
-
         aaa = glob.glob("/mnt/SOURCE/packages/a/aaa_base-*.t?z")[0]
         self.installpkg(aaa, "/mnt/TARGET")
 
         installed at last, or for post install required tasks.
 
         """
-        if not self._check_continue():
-            return
-
+        theme = glob.glob("/mnt/SOURCE/packages/xconf/VL_Theme-*.t?z")
         vlconfig = glob.glob("/mnt/SOURCE/packages/a/vlconfig2-*.t?z")[0]
+        for pkg in theme:
+            self.installpkg(pkg, "/mnt/TARGET")
         self.installpkg(vlconfig, "/mnt/TARGET")
 
     def install_kernel(self):
         Represented in VINSTALL.INI as "sata=version"
 
         """
-        if not self._check_continue():
-            return
-
         install_media = self.config["install_media"]
         kernel_version = install_media.config.get("kernels", "sata")
         kernel_version = kernel_version.replace("\"", "")
         """Create and install a fstab file in disk
 
         """
-        print "Installing fstab in", disk
-        if not self._check_continue():
-            return
-
         fstab_obj = fstab.Fstab("/mnt/TARGET/etc/fstab")
         root_entry = fstab.FstabEntry(device = "%s1" % disk.path(),
                 mountpoint = "/", filesystem = self.ROOT_FS)
                 mountpoint = "none", filesystem="swap")
         for entry in (root_entry, swap_entry):
             fstab_obj.add_entry(entry)
+        # Additional entries needed
+        proc = fstab.FstabEntry(device="proc",
+            mountpoint = "/proc",
+            filesystem = "proc",
+            options = "defaults 0 0")
+        devpts = fstab.FstabEntry(device="none",
+            mountpoint = "/dev/pts",
+            filesystem = "devpts",
+            options = "gid=5,mode=666  0 0"
+            )
+        shm = fstab.FstabEntry(device = "tmpfs",
+            mountpoint = "/dev/shm",
+            filesystem = "tmpfs",
+            options = "defaults 0 0"
+            )
+        sysfs = fstab.FstabEntry(device = "sysfs",
+            mountpoint = "/sys",
+            filesystem = "sysfs",
+            options = "defaults 0 0"
+            )
+        for item in (proc, devpts, shm, sysfs):
+            fstab_obj.add_entry(item)
 
     def setup_services(self):
         """Setup default system services
         """
         #XXX:  Define the default set of services for each runlevel
         # At some other location so they can be used by advanced.py too
-
-        if not self._check_continue():
-            return
-
         svcbasic = [ "cron", "portmap", "lm_sensors", "gpm", "inetd" ]
         svc2 = svcbasic[:]
         svc3 = svcbasic[:]
         """Install the default bootloader in disk
 
         """
-        print "Installing bootloader in", disk
-
-        if not self._check_continue():
-            return
-
-        vga_size = "800x600"
-        vga_quality = "16"
-        timeout = 5
-        grub = bootloader.Grub2(target=disk.path(), vga_size=vga_size,
-                vga_quality=vga_quality, timeout=timeout)
+        grub = bootloader.Grub2(target=disk.path())
         with utils.Chroot("/mnt/TARGET"):
-            grub.write_config("/etc/default/grub")
+            grub.write_config()
             grub.install()
 
+    def install_kernels(self):
+        """Copy the kernels from the initrd to the target"""
+        src = self.config["install_media"]
+        kver = src.config.get("kernels", "sata").replace("\"","")
+        ksrc = os.path.join('/mnt', 'SOURCE', 'isolinux','kernel', 'sata')
+        ktgt = os.path.join('/mnt', 'TARGET', 'boot', 'vmlinuz-%s'% kver)
+        shutil.copyfile(ksrc, ktgt)
+
     def vector_version(self):
         """write /etc/vector-version based on the information
         in VINSTALL.INI
 
         """
-        print "Writing vector-version"
-
-        if not self._check_continue():
-            return
-
         install_media = self.config["install_media"]
         distro = install_media.config.get("general", "distro").replace("\"", "")
         version = install_media.config.get("general", "version").replace("\"", "")
         Later steps will need this when we chroot there
 
         """
-        if not self._check_continue():
-            return
-
         for point in ("sys","proc","dev"):
             cmd = "mount -o bind /%s /mnt/TARGET/%s" % (point, point)
             subprocess.check_call(cmd.split())
         """Clear the mountpoints that were mounted for the configuration
 
         """
-        if not self._check_continue():
-            return
-
         for point in ("sys","proc","dev"):
             cmd = "umount -f /mnt/TARGET/%s" % point
             subprocess.check_call(cmd.split())
 
-    def _check_continue(self):
-        return 'I_MEAN_IT' in os.environ
 
-
-
-class Setup(Controller):
+class UserSetup(object):
     """Minimal configuration - Ask for username and passwords
 
     """
         return Render(title, intro, username, password, rootpassword)
 
     def next(self):
-        """This is the last stage in automatic mode, return None
+        """Return next step
 
         """
         return None
 
     def previous(self):
-        """Return the previous step
+        """Return previous step
 
         """
         return AutomaticMode
         """Create a user and setup passwords
 
         """
-        yield self.set_root_password, (rootpassword,), "Creating users"
-        yield self.create_user_account, (username, password), "Setting passwords"
+        if rootpassword.strip():
+            yield self.set_root_password, (rootpassword,), "Creating users"
+        if username.strip() and password.strip():
+            yield self.create_user_account, (username, password), \
+                    "Setting passwords"
 
     def set_root_password(self, rootpassword):
         """Set the root password
 
         """
-        if not self._check_continue():
-            return
-
         admin = users.User()
         admin.root = "/mnt/TARGET"
         admin.login = "root"
         """Create a user account and set its password
 
         """
-        if not self._check_continue():
-            return
-
         account = users.User()
         account.root = "/mnt/TARGET"
         account.login = username
         with utils.Chroot("/mnt/TARGET"):
             account.create()
 
-    def _check_continue(self):
-        return 'I_MEAN_IT' in os.environ
-
-

vinstall/controller/bootloader.py

 
 
 from vinstall.core import Application, Render, Controller, model
-from vinstall.backend.media import Disk
-import os
+from vinstall.backend.media import Disk, Partition
+from vinstall.backend import bootloader, utils
+from itertools import chain
+import os, subprocess
 
 
 class Bootloader(Controller):
     """Select a bootloader
 
     """
-    def find_disks(self):
+    def init(self):
         """Find the disks available in the system.
 
         """
-        return [ i for i in Disk.all() if not i.is_read_only() ]
+        self.disks = [ i for i in Disk.all() if not i.is_read_only() ]
+        self.partitions = [ i for i in Partition.all() if i.query_filesystem()
+                != "xfs" ]
+        self.targets = self.disks + self.partitions
+        self.bootloaders = [ u"Lilo", u"Grub2", u"None" ]
 
     def render(self):
         """Show bootloader options
 
         bootloaders = model.DropdownOptionList()
         bootloaders.label = u"Select a bootloader"
-        bootloaders.add_option(u"Lilo")
-        bootloaders.add_option(u"Grub")
-        bootloaders.add_option(u"None")
-
-        resolutions = model.DropdownOptionList()
-        resolutions.label = u"Set the boot resolution to"
-        resolutions.add_option(u"1024x768")
-        resolutions.add_option(u"1280x1024")
-        resolutions.add_option(u"800x600")
+        for i in self.bootloaders:
+            bootloaders.add_option(i)
 
         location = model.DropdownOptionList()
         location.label = u"Install the bootloader in"
-        for i in self.find_disks():
+        for i in self.targets:
             location.add_option(unicode(i))
 
-        return Render(title, intro, bootloaders, resolutions, location)
+        return Render(title, intro, bootloaders, location)
 
     def next(self):
         """Return next step
 
         """
+        return None
+
+    def previous(self):
+        """Return previous step
+
+        """
         from vinstall.controller import usersetup
         return usersetup.UserSetup
 
-    def previous(self):
+    def command(self, bootloader, target):
+        """Schedule command for later execution
+
         """
+        yield self.set_bind_mounts, tuple(), "Mounting pseudo filesystems"
+        yield self.install_bootloader, (bootloader, target), "Installing bootloader"
+        yield self.clear_bind_mounts, tuple(), "Clearing bind mounts"
+
+    def install_bootloader(self, bloader, target):
+        """Install the bootloader
 
         """
-        from vinstall.controller import packages
-        return packages.PackageSelection
+        bloader = self.bootloaders[bloader]
+        disk = self.targets[target]
+        target = disk.path()
+
+        if bloader.lower() == "lilo":
+            bl = bootloader.Lilo(target)
+            for system in bootloader.OperatingSystem.all():
+                bl.add_os(system)
+        elif bloader == "Grub2":
+            bl = bootloader.Grub2(target)
+        else:
+            return
+
+        with utils.Chroot("/mnt/TARGET"):
+            bl.backup_config()
+            bl.write_config()
+            bl.install()
+
+    def set_bind_mounts(self):
+        """Bind mount some paths into the target.
+        Later steps will need this when we chroot there
 
-    def command(self, bootloader, resolution, target):
-        """Schedule command for later execution
+        """
+        for point in ("sys","proc","dev"):
+            cmd = "mount -o bind /%s /mnt/TARGET/%s" % (point, point)
+            subprocess.check_call(cmd.split())
+
+    def clear_bind_mounts(self):
+        """Clear the mountpoints that were mounted for the configuration
 
         """
+        for mpoint in ("sys","proc","dev"):
+            cmd = "umount -f /mnt/TARGET/%s" % mpoint
+            subprocess.check_call(cmd.split())
+

vinstall/controller/mountpoints.py

 
 from vinstall.core import Application, Render, Controller, model
 from vinstall.backend.media import Partition
+from vinstall.backend import mountpoints
 import os
 
 
     """Setup target locations
 
     """
-    def find_partitions(self):
-        """Return available partitions
-
-        """
-        return Partition.all()
+    def init(self):
+        self.config["mountpoints"] = []
 
     def render(self):
         """Show mount options
 
         """
         title = "Mountpoints"
-        intro = "Setup mountpoints for your new system"
-        mountpoints = ("home", "usr", "var", "boot", "tmp", "opt", "bin")
-        partitions = self.find_partitions()
-        options = []
-        for i in self.find_partitions():
-            option = model.DropdownOptionList()
-            option.label = unicode(i)
-            option.add_option("Do not mount")
-            for j in mountpoints:
-                option.add_option(j)
-            options.append(option)
-        return Render(title, intro, *options)
+        intro = "Setup mountpoints for your new system.\n"
+        self.render = Render(title, intro, mountpoints.MountPoints())
+        return self.render
+        #return Render(title, intro, mountpoints.MountPoints())
 
     def next(self):
         """Return next step
 
         """
+        print self.render.main_window
         from vinstall.controller import packages
         return packages.PackageSelection
 
         from vinstall.controller import advanced
         return advanced.AdvancedMode
 
-    def process(self, *mountpoints):
-        """React to user input
+    def command(self, mountpoints):
+        """Schedule command for later execution
+        Args:
+            - mountpoints: a mapping of partition => [mountpoint, filesystem]
+
+        If mountpoint is None, partition shouldnt be mounted
+        If filesystem is None, partition shouldnt be formatted
 
         """
+        to_format = []
+        to_mount = []
+        for partition, (mount, fs) in mountpoints.items():
+            if fs:
+                to_format.append((partition, fs))
+            if mount:
+                to_mount.append((partition, mount))
+                self.store_mountpoint(partition, mount, fs)
+        yield self.format_partitions, (to_format,), "Formating partitions"
+        yield self.mount_target, (to_mount,), "Mounting target"
+
+    def mount_target(self, data):
+        """Mount target partitions
 
-    def command(self, *mountpoints):
-        """Schedule command for later execution
+        """
+        #XXX better data structure for not iterating twice
+        target = "/mnt/TARGET"
+        if not os.path.exists(target):
+            os.mkdir(target)
+        root = next(p for p, m in data if m == "/")
+        root.mount(target)
+        for partition, mountpoint in data:
+            if mountpoint not in ("/", "swap"):
+                d = os.path.join(target, mountpoint)
+                if not os.path.exists(d):
+                    os.mkdir(d)
+                partition.mount(d)
+
+    def format_partitions(self, data):
+        """Format partitions
 
         """
+        for partition, filesystem in data:
+            if partition.is_mounted():
+                partition.umount()
+            partition.format(filesystem)
+
+    def store_mountpoint(self, device, mountpoint, fs):
+        """Store mount in the config object for writting to fstab later
+
+        """
+        mount = (device, mountpoint, fs)
+        self.config["mountpoints"].append(mount)
+

vinstall/controller/packages.py

 
 
 from vinstall.core import Application, Render, Controller, model
-import os
+from vinstall.backend import utils
+from itertools import chain
+import os, shutil, glob, subprocess
 
 
 class PackageSelection(Controller):
     """Select packages to be installed in the target
 
     """
+    def init(self):
+        install_media = self.config["install_media"]
+        self.required_bulks = install_media.items("bulks/required")
+        self.optional_bulks = install_media.items("bulks/optional")
+        self.mount_source()
+
     def render(self):
         """Show package options
 
         """
         title = "Package selection"
         intro = "Select packages to be installed in your system"
-        options = model.BoolOption("Install X11")
-        return Render(title, intro, options)
+        options = [ model.BoolOption(v) for k, v in self.optional_bulks ]
+        return Render(title, intro, *options)
 
     def next(self):
         """Return next step
 
         """
-        from vinstall.controller import bootloader
-        return bootloader.Bootloader
+        from vinstall.controller import usersetup
+        return usersetup.UserSetup
 
     def previous(self):
         """
         from vinstall.controller import mountpoints
         return mountpoints.Mountpoint
 
-    def process(self, boolean):
-        """Process user input
+    def command(self, *bulks):
+        """Schedule command for later execution
 
         """
+        yield self.preinstall, tuple(), "Preparing package install"
+        for package in self.packages(bulks):
+            yield self.installpkg, (package,), "Installing %s" % package.split("/")[-1]
+        yield self.postinstall, tuple(), "Installing setup routines"
+        yield self.install_kernel, tuple(), "Installing Linux kernel"
+        yield self.vector_version, tuple(), "Writting /etc/vector-version"
 
-    def command(self, boolean):
-        """Schedule command for later execution
+    def packages(self, bulks):
+        """Return packages to be installed by chaining required packages with
+        selected bulks
+
+        """
+        required_bulks = [ i[0] for i in self.required_bulks ]
+        optional_bulks = [ self.optional_bulks[i][0] for i, e in enumerate(bulks) if e ]
+        bulks = chain(required_bulks, optional_bulks)
+        for b in bulks:
+            for package in self.bulk_packages(b):
+                yield package
+
+    def installpkg(self, package):
+        """Install a package in the target
+
+        """
+        command = "/usr/sbin/installpkg %s --root=%s" % (package, "/mnt/TARGET")
+        subprocess.check_call(command.split())
+
+    def preinstall(self):
+        """Run before installing packages
+
+        """
+        aaa = glob.glob("/mnt/SOURCE/packages/a/aaa_base-*.t?z")[0]
+        self.installpkg(aaa)
+
+    def postinstall(self):
+        """Run after installing packages
+
+        """
+        theme = glob.glob("/mnt/SOURCE/packages/*/VL_Theme-*.t?z")
+        vlconfig = glob.glob("/mnt/SOURCE/packages/a/vlconfig2-*.t?z")[0]
+        for pkg in theme:
+            self.installpkg(pkg)
+        self.installpkg(vlconfig)
+
+    def install_kernel(self):
+        """Install kernel packages
+
+        """
+        install_media = self.config["install_media"]
+        kernel_version = install_media.config.get("kernels", "sata")
+        kernel_version = kernel_version.replace("\"", "")
+        source = os.path.join('/mnt', 'SOURCE', 'isolinux', 'kernel',
+                'sata')
+        target = os.path.join('/mnt', 'TARGET', 'boot', 'vmlinuz-%s' %
+            kernel_version)
+        shutil.copyfile(source, target)
+
+    def bulk_packages(self, bulk):
+        """Return packages in bulk
+
+        """
+        excluded =("vlconfig2", "vlsetup", "aaa_base", "VL_Theme")
+        basedir = "/mnt/SOURCE/veclinux"
+        pkgbasedir = "/mnt/SOURCE/packages"
+        path = os.path.join(basedir, bulk)
+        with open(path) as f:
+            for packagename in f:
+                if packagename in excluded:
+                    continue
+                #XXX do not use glob this way, god forgive us
+                packagename = packagename.strip()
+                fname = "%s/*/%s*t?z" % (pkgbasedir, packagename)
+                fullpath = glob.glob(fname)
+                if fullpath:
+                    for p in fullpath:
+                        f = p.rsplit("/", 1)[1]
+                        name, _, _, _ = f.rsplit("-", 3)
+                        if name == packagename:
+                            yield p
+                else:
+                    print "WARNING: missing package", packagename
+
+    def vector_version(self):
+        """write /etc/vector-version based on the information
+        in VINSTALL.INI
+
+        """
+        install_media = self.config["install_media"]
+        distro = install_media.config.get("general", "distro").replace("\"", "")
+        version = install_media.config.get("general", "version").replace("\"", "")
+        build_date = install_media.config.get("general",
+                "build_date").replace("\"", "")
+        vector_version =  "%s built on %s\n" % (version, build_date)
+        with open("/mnt/TARGET/etc/vector-version", 'w') as f:
+            f.write(vector_version)
+
+    def mount_source(self):
+        """Mount the install media, needed for looking for available packages
 
         """
+        mountpoint = "/mnt/SOURCE"
+        if not os.path.exists(mountpoint):
+            os.mkdir(mountpoint)
+        install_media = self.config["install_media"]
+        if install_media.is_iso():
+            if not os.path.exists(install_media.path):
+                print "mount container here"
+		mntpoint = install_media.path.rsplit("/", 1)[0]
+		dev = mntpoint.replace("/mnt", "/dev")
+            	utils.mount(dev, mntpoint)
+            utils.mountiso(install_media.path, mountpoint)
+        else:
+            utils.mount(install_media.path, mountpoint, filesystem="auto")

vinstall/controller/usersetup.py

 
 
 from vinstall.core import Application, Render, Controller, model
-import os
+from vinstall.backend import users, utils, fstab, service
+import os, subprocess
 
 
 class UserSetup(Controller):
         return Render(title, intro, username, password, rootpassword)
 
     def next(self):
-        """Return None, routine ends here
+        """Return next step
 
         """
-        return None
+        from vinstall.controller import bootloader
+        return bootloader.Bootloader
 
     def previous(self):
         """Return previous step
 
         """
-        from vinstall.controller import bootloader
-        return bootloader.Bootloader
+        from vinstall.controller import packages
+        return packages.PackageSelection
 
     def command(self, username, password, rootpassword):
-        """Schedule command for later execution
+        """Create a user and setup passwords
+
+        """
+        if rootpassword.strip():
+            yield self.set_root_password, (rootpassword,), "Creating users"
+        if username.strip() and password.strip():
+            yield self.create_user_account, (username, password), "Setting passwords"
+        yield self.fstab, tuple(), "Creating fstab"
+        yield self.setup_services, tuple(), "Setting up services"
+
+    def set_root_password(self, rootpassword):
+        """Set the root password
+
+        """
+        admin = users.User()
+        admin.root = "/mnt/TARGET"
+        admin.login = "root"
+        with utils.Chroot("/mnt/TARGET"):
+            admin.change_password(rootpassword)
+
+    def create_user_account(self, username, userpassword):
+        """Create a user account and set its password
 
         """
+        account = users.User()
+        account.root = "/mnt/TARGET"
+        account.login = username
+        account.password = userpassword
+        with utils.Chroot("/mnt/TARGET"):
+            account.create()
+
+    def fstab(self):
+        """Create and install a fstab file in disk
+
+        """
+        fstab_obj = fstab.Fstab("/mnt/TARGET/etc/fstab")
+
+        mountpoints = self.config.get("mountpoints")
+        for dev, mount, fs in mountpoints:
+            if not fs:
+                fs = dev.query_filesystem()
+
+            entry = fstab.FstabEntry(
+                    device=dev.device_path,
+                    mountpoint=mount,
+                    filesystem=fs)
+            fstab_obj.add_entry(entry)
+        # Additional entries needed
+        proc = fstab.FstabEntry(device="proc",
+            mountpoint = "/proc",
+            filesystem = "proc",
+            options = "defaults 0 0")
+        devpts = fstab.FstabEntry(device="none",
+            mountpoint = "/dev/pts",
+            filesystem = "devpts",
+            options = "gid=5,mode=666  0 0"
+            )
+        shm = fstab.FstabEntry(device = "tmpfs",
+            mountpoint = "/dev/shm",
+            filesystem = "tmpfs",
+            options = "defaults 0 0"
+            )
+        sysfs = fstab.FstabEntry(device = "sysfs",
+            mountpoint = "/sys",
+            filesystem = "sysfs",
+            options = "defaults 0 0"
+            )
+        for item in (proc, devpts, shm, sysfs):
+            fstab_obj.add_entry(item)
+            
+
+    def setup_services(self):
+        """Setup default system services
+
+        """
+        svcbasic = [ "cron", "portmap", "lm_sensors", "gpm", "inetd" ]
+        svc2 = svcbasic[:]
+        svc3 = svcbasic[:]
+        svc3.extend(["samba", "cups", "sshd"])
+        svc4 = svcbasic[:]
+        svc4.extend(["bluetooth", "fuse", "cups"])
+        svc5 = svc3[:]
+        svc5.extend(["bluetooth", "fuse"])
+
+        for svc in service.Service.all("/mnt/TARGET"):
+            for s, runlevel in ((svc2, 2), (svc3, 3), (svc4, 4), (svc5, 5)):
+                if svc.name in s:
+                    svc.enable(runlevel)
+

vinstall/core/wizard.py

+# coding: utf8
+
+"""VASM application toolkit prototype"""
+
+
+import Queue
+import sys
+from inspect import isgeneratorfunction
+from .command import ProcessingFacade
+
+
+class WizardApplication(object):
+    "Wizard App"
+
+
+    def __init__(self, first_controller, config=None):
+        self.config = config or {}
+        self.controller = first_controller
+        self.protocol = WizardProtocol()
+        self.protocol.factory = self
+
+    def set_view(self, view):
+        "Set the view for this app"
+        mod_name = "vinstall.ui"
+
+        try:
+            module = __import__(mod_name, globals(), locals(), [view],
+                    level=1)
+        except ImportError:
+            #2.7 behaves differently
+            module = __import__(mod_name, globals(), locals(), [view],
+                    level=0)
+        sys.modules[mod_name] = module
+
+    def make_controller_instance(self, klass):
+        """Create an instance of the controller class
+
+        """
+        obj = klass()
+        obj.config = self.config
+        init_method = getattr(obj, "init", None)
+        if init_method:
+            init_method()
+        return obj
+
+    def run(self):
+        controller = self.make_controller_instance(self.controller)
+        self.protocol.start(controller)
+
+    def stop(self):
+        self.protocol.stop()
+        sys.exit(0)
+
+
+class WizardProtocol(object):
+    """Controller protocol for wizard-like applications
+    This object is responsible for processing each controller object thats part
+    of the wizard.
+
+    """
+    def __init__(self):
+        self.factory = None
+        self.current_controller = None
+        self.current_render = None
+        self.commands = []
+
+    def process_state(self, controller):
+        "Process the current state of the wizard"
+        self.current_controller = controller
+        self.show()
+
+    def show(self):
+        "Present the current controller to the user"
+        self.current_render = render = self.current_controller.render()
+        render.main_window.clear_callbacks()
+        render.update_main_window()
+        render.main_window.add_next_callback(self.next)
+        render.main_window.add_previous_callback(self.previous)
+
+    def run_controller(self):
+        "Execute tasks specified by the controller"
+        process_method = getattr(self.current_controller, "process", None)
+        args = self.current_render.get_user_input()
+        if process_method:
+            self.execute(process_method, args, {})
+        command_method = getattr(self.current_controller, "command", None)
+        if command_method:
+            self.commands.append((command_method, args))
+
+    def next(self, *_):
+        "Jump to the next state of the application"
+        self.run_controller()
+        next_controller = self.current_controller.next()
+        if next_controller:
+            instance = self.factory.make_controller_instance(next_controller)
+            self.process_state(instance)
+        else:
+            self.process_task_queue()
+
+    def previous(self, *_):
+        "Jump back to previous controller"
+        controller = self.current_controller.previous()
+        if controller:
+            command = getattr(controller, "command", None)
+            if command:
+                self.commands.pop()
+            instance = self.factory.make_controller_instance(controller)
+            self.process_state(instance)
+        else:
+            self.stop()
+
+    def execute(self, a_callable, args, kwargs):
+        "Execute callable"
+        a_callable(*args, **kwargs)
+
+    def process_task_queue(self):
+        """Execute tasks scheduled in the command list.
+        We will inject a last controller for showing progress and reporting
+        task completion, and set callbacks for quiting.
+
+        """
+        self.current_render.main_window.disable_buttons()
+        queue = Queue.Queue()
+        processing = ProcessingFacade(queue)
+        for func, args in self.commands:
+            if isgeneratorfunction(func):
+                for f, a, desc in func(*args):
+                    processing.add_command(f, args=a, description=desc)
+            else:
+                processing.add_command(func, args=args,
+                        description=func.__doc__)
+
+        def tasks_finished_callback():
+            self.current_render.main_window.set_next_button_label("Reboot")
+            self.current_render.main_window.set_previous_button_label("Quit")
+            self.current_render.main_window.enable_buttons()
+            #self.current_render.main_window.alert("Installation finished")
+
+        processing.add_command(tasks_finished_callback, args=tuple(),
+                description="All tasks done")
+
+        Render = type(self.current_render)
+
+        class ProcessingController(object):
+
+            def render(self):
+                return Render("Processing tasks", ("Please wait until we "
+                        "process the requested operations"), processing)
+
+            def next(self):
+                return None
+
+            def previous(self):
+                return None
+
+        controller = self.factory.make_controller_instance(ProcessingController)
+        self.process_state(controller)
+        self.current_render.main_window.clear_callbacks()
+        self.current_render.main_window.add_next_callback(self.stop)
+        self.current_render.main_window.add_previous_callback(self.stop)
+        processing.execute_all()
+
+    def start(self, first_controller):
+        "Start the main loop"
+        self.process_state(first_controller)
+        self.current_render.main_window.run()
+
+    def stop(self, *_):
+        "Exit the wizard"
+        self.current_render.main_window.stop()

vinstall/installer.py

 
 import sys
 from vinstall.core.application import Application
+from vinstall.core.wizard import WizardApplication
 from vinstall.controller import intro, sourceselection
 from vinstall.backend.installmedia import InstallMedia
 
     else:
         first = intro.Introduction
         config["install_media"] = sources.pop()
-    application = Application(view, first, config=config)
+    application = WizardApplication(first, config=config)
+    application.set_view(view)
     application.run()
 

vinstall/ui/gtk.py

 
 from __future__ import absolute_import
 
-import gtk
+import gtk, time
 from vinstall.core import core, model, command
+from vinstall.backend import mountpoints
 
 
 gtk.gdk.threads_init()