Browse Source

merged in changes from sidu-installer2

refs #1343
next
Alf Gaida 7 years ago
parent
commit
ad99f92ff3
68 changed files with 5409 additions and 1 deletions
  1. 7
    0
      .gitignore
  2. 19
    0
      .project
  3. 14
    0
      .pydevproject
  4. 567
    0
      backend/autopart.pl
  5. 22
    0
      backend/autopart.sh
  6. 26
    0
      bin/sidu-installer-control
  7. 230
    0
      config/sidu-installer_en.conf
  8. BIN
      data/config.db
  9. 1
    1
      debian/changelog
  10. 4
    0
      import_config.sh
  11. 33
    0
      installer/.coverage
  12. 0
    0
      installer/__init__.py
  13. 21
    0
      installer/models.py
  14. BIN
      installer/static/favicon.ico
  15. BIN
      installer/static/images/header-logo.png
  16. BIN
      installer/static/images/header.png
  17. 4
    0
      installer/static/images/index.html
  18. BIN
      installer/static/images/logo.png
  19. BIN
      installer/static/images/usb_stick.png
  20. BIN
      installer/static/images/usbstick.png
  21. BIN
      installer/static/images/warning.png
  22. 165
    0
      installer/static/siduction.css
  23. 25
    0
      installer/static/siduction.js
  24. 73
    0
      installer/tests.py
  25. 120
    0
      installer/views.py
  26. 0
    0
      isource/__init__.py
  27. 432
    0
      isource/autopartpage.py
  28. 144
    0
      isource/bootpage.py
  29. 82
    0
      isource/checkpage.py
  30. 556
    0
      isource/diskinfopage.py
  31. 52
    0
      isource/globalpage.py
  32. 94
    0
      isource/homepage.py
  33. 56
    0
      isource/languagepage.py
  34. 201
    0
      isource/mountpointpage.py
  35. 71
    0
      isource/networkpage.py
  36. 205
    0
      isource/packetpage.py
  37. 112
    0
      isource/partitionpage.py
  38. 106
    0
      isource/rootfspage.py
  39. 319
    0
      isource/runpage.py
  40. 33
    0
      isource/session.py
  41. 131
    0
      isource/userpage.py
  42. 10
    0
      manage.py
  43. 0
    0
      sidu-installer/__init__.py
  44. 181
    0
      sidu-installer/settings.py
  45. 28
    0
      sidu-installer/urls.py
  46. 28
    0
      sidu-installer/wsgi.py
  47. 133
    0
      templates/autopart.snippets
  48. 52
    0
      templates/boot.snippets
  49. 34
    0
      templates/check.snippets
  50. 108
    0
      templates/diskinfo.snippets
  51. 32
    0
      templates/home.snippets
  52. 23
    0
      templates/language.snippets
  53. 59
    0
      templates/menu.snippets
  54. 99
    0
      templates/mountpoint.snippets
  55. 30
    0
      templates/network.snippets
  56. 62
    0
      templates/packet.snippets
  57. 30
    0
      templates/pageframe.html
  58. 49
    0
      templates/partition.snippets
  59. 46
    0
      templates/rootfs.snippets
  60. 72
    0
      templates/run.snippets
  61. 19
    0
      templates/search.snippets
  62. 88
    0
      templates/user.snippets
  63. 36
    0
      templates/wait.snippets
  64. BIN
      unittest/.coverage
  65. 19
    0
      unittest/coverage.sh
  66. 133
    0
      unittest/diskpagetest.py
  67. 30
    0
      unittest/globalpagetest.py
  68. 83
    0
      unittest/sessiontest.py

+ 7
- 0
.gitignore View File

@@ -1,3 +1,10 @@
# don't track debian build stuff
/debian/files
/debian/*log
/debian/*substvars
/debian/sidu-installer


sqlite.db
*.pyc
installer/static/public

+ 19
- 0
.project View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>sidu-installer</name>
<comment></comment>
<projects>
<project>sidu-base</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
<nature>org.python.pydev.django.djangoNature</nature>
</natures>
</projectDescription>

+ 14
- 0
.pydevproject View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?>

<pydev_project>
<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
<key>DJANGO_MANAGE_LOCATION</key>
<value>manage.py</value>
</pydev_variables_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/sidu-installer</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

+ 567
- 0
backend/autopart.pl View File

@@ -0,0 +1,567 @@
#! /usr/bin/perl
#
# Usage: autopart.pl "$CMD" "$ANSWER" "$DISKS" "$ALLOW_INIT" "$PARTS" "$VG_INFO" "$LV_INFO"

use strict;

my $cmd = shift;
my $answer = shift;
# sdb:mbr+sdc:gpt
my $diskInfo = shift;
# YES or NO
my $s_allowInit = shift;
# sdb1-2048-9999+sdb2-10000
my $partitions = shift;
# siduction:32M
my $vgInfo = shift;
# root:rider32:4G:ext4;home:home:2G:ext4;swap:swap:400M:swap
my $lvInfo = shift;
# progress file, e.g. /tmp/xy.progress
my $s_fnProgress = shift;
$s_fnProgress = "/tmp/autopart.progress" unless $s_fnProgress;

# progress: max. number of steps (progress)
my $s_maxTasks = 5;
# done number of steps
my $s_currTask = 0;

my $s_errors = 0;

# Constants:
my $MBR = "mbr";
my $GPT = "gpt";

my $s_fdisk = "/sbin/fdisk";
my $s_gdisk = "/sbin/gdisk";
my @s_logLines;

my %s_wantedDiskType;
my %s_realDiskType;
# name => "ptype:mdb part:1-1024-8000:2-8001-16000 ext:2"
my %s_diskInfo;

&Progress("initialization");
&StorePartTypeInfo($diskInfo);

if ($cmd eq "stdlvm"){
if (! VGExists($vgInfo)){
&Progress("creating partitions");
&BuildLVMs($partitions);
if ($s_errors > 0){
Error ("task was aborted due to errors");
} else {
&Progress("creating volume group");
BuildVG($partitions, $vgInfo);
&Progress("creating logical volumes");
BuildLVs($lvInfo, $vgInfo);
}
}
} else {
&Error("unknown command: $cmd");
exit 1;
}
&Progress("writing info", 1);
my $temp = WriteFile(join("", @s_logLines), ".log");
Exec("mv $temp $answer");
if (! -f $answer){
die "+++ $temp -> $answer failed: $!";
}
exit(0);

# Tests whether a volume group exists.
# @param vgInfo e.g. siduction:4M
# @return 0: vg does not exist 1: vg exists
sub VGExists{
my $vgInfo = shift;
my ($name, $extSize) = split(/:/, $vgInfo);
my $rc = -d "/dev/$name";
if ($rc){
Error("VG $name already exists. (/dev/$name exists)");
}
return $rc;
}
# Builds a logical volume
# @param name boot, root, home or swap
# @param label file system label
# @param size e.g. 4G or *
# @param filesys e.g. ext4
# @param vg name of the volume group
sub BuildLV{
my $name = shift;
my $label = shift;
my $size = shift;
my $fs = shift;
my $vg = shift;
$size = $size eq "*" ? "--extents 100%FREE" : "--size $size";
Exec("lvcreate $size --name $name $vg", 1);
my $lvPath = "/dev/mapper/$vg-$name";
if (! -e $lvPath){
Error("LV $name not created");
} elsif ($fs eq "swap"){
Exec("mkswap -L $label /dev/mapper/$vg-$name", 1);
Log("=== LV $name created as swap device");
} else {
my $fsFull = qx(which mkfs.$fs);
if ($fsFull eq ""){
Error("unknown filesystem: $fs");
} else {
Exec("mkfs.$fs -L $label $lvPath");
Log("=== LV $name formatted with $fs");
}
}
}

# Converts a value to KiByte
# @param value examples: 102G 3T 8k 2M
# @return amount of KiByte
sub toKiByte{
my $value = shift;
my $number = 0;
my $unit;
if ($value =~ /(\d+)(.)/){
($number, $unit) = ($1, $2);
$unit =~ tr/a-z/A-Z/;
if ($unit eq "G"){
$number *= 1024*1024;
} elsif ($unit eq "T"){
$number *= 1024*1024*1024;
} elsif ($unit eq "K"){
$number *= 1;
} else {
$number *= 1024;
}
}
return $number
}

# Converts a amount of KiBytes into a number and a unit.
# @param kiByte amount in KiBytes
# @return e.g. 4M or 243K or 22G
sub kiByteToSize{
my $kiByte = shift;
my $size;
if ($kiByte % (1024 * 1024) == 0){
$size = int($kiByte / 1024 / 1024) . "G";
} elsif ($kiByte % (1024) == 0){
$size = int($kiByte / 1024) . "M";
} else {
$size = (0 + $kiByte) . "K";
}
return $size;
}
# Builds all logical volumes:
# @param lvInfo e.g. root:rider32:4G:ext4;swap:swap:400M:swap
# @param vgInfo e.g. siduction:4M
sub BuildLVs{
my $lvInfo = shift;
my $vgInfo = shift;
my ($vg, $extSize) = split(/:/, $vgInfo);
$extSize = toKiByte($extSize);
my @lvs = split(/;/, $lvInfo);
foreach(@lvs){
my ($lv, $name, $size, $fs) = split(/:/);
if ($size ne "*"){
$size = toKiByte($size);
$size = kiByteToSize(int($size / $extSize) * $extSize);
}
&Progress("creating $name");
BuildLV($lv, $name, $size, $fs, $vg);
}
}

# Builds a volume group.
# @param parts the partion info of the disk
# @param vgInfo e.g. "siduction:32M"
sub BuildVG{
my $parts = shift;
my $vgInfo = shift;
my ($vg, $extSize) = split(/:/, $vgInfo);
# Initialize the PV:
my $cmd = "";
my @parts = split(/\+/, $parts);
my $pvList = "";
foreach(@parts){
my @cols = split(/-/);
$pvList .= " /dev/" . $cols[0];
}
Exec("pvcreate --yes $pvList", 1);
Exec("vgcreate --physicalextentsize $extSize $vg $pvList", 1);
}

# Reads the disk info with fdisk.
# The info will be stored in %s_diskInfo
# @param disk e.g. sdb
sub ReadFdiskInfo{
my $disk = shift;
my $info = "ptype:$MBR";
open(DISK, "$s_fdisk -l /dev/$disk|") || die "$s_fdisk -l /dev/$disk";
my @lines = <DISK>;
close DISK;
my $parts;
foreach(@lines){
#/dev/sdb1 2048 10000 3976+ 83 Linux
# if (m!^/dev/\D+(\d+)\D+(\d+)\s+(\d+)!){
if (m!^/dev/\D+(\d+)\D+(\d+)\s+(\d+)!){
if ($parts eq ""){
$parts = " part";
}
$parts .= ":$1-$2-$3";
}
if (m!^/dev/\D+(\d+).*Extended!){
$info .= " ext:$1";
}
}
$info .= $parts;
$s_diskInfo{$disk} = $info;
return $info;
}

# Reads the disk info with gdisk.
# The info will be stored in %s_diskInfo
# @param disk e.g. sdb
sub ReadGdiskInfo{
my $disk = shift;
my $info = "ptype: gpt";
open(DISK, "$s_gdisk -l /dev/$disk|") || die "$s_gdisk -l /dev/$disk";
my @lines = <DISK>;
close DISK;
my $parts;
foreach(@lines){
if (/^\s+(\d+)\s+(\d+)\s+(\d+)/){
if ($parts eq ""){
$parts = " part";
}
$parts .= ":$1-$2-$3";
}
}
$info .= $parts;
$s_diskInfo{$disk} = $info;
return $info;
}

# Returns the disk info.
# If not known it will be read.
# @param disk e.g. sdb
# @return the disk info
sub GetDiskInfo{
my $disk = shift;
my $rc = $s_diskInfo{$disk};
if ($rc eq ""){
my $type = FindDiskType($disk);
$s_diskInfo{$disk} = $type;
if ($type eq $MBR){
$rc = ReadFdiskInfo($disk);
} elsif ($type eq $GPT){
$rc = ReadGdiskInfo($disk);
} elsif ($type eq "!"){
# error already is displayed
} else{
Error("unknown partition table: $disk");
}
}
return $rc;
}

# Returns the class of the next new partition.
# @param disk disk to test
# @result "p": primary "l": logical "b": primary or logical
sub GetNewPartClass{
my $disk = shift;
my $info = $s_diskInfo{$disk};
my $rc;
if ($info =~ /ptype:$MBR/){
if ($info !~ /ext:/){
$rc = "p";
} else {
$rc = ($info =~ /:1-.*:2-.*:3-.*:4-/) ? "l" : "b";
}
} else {
Error("not implemented: GetNewPartClass gpt");
}
return $rc;
}
# Stores the partition types of the disks.
# @param info e.g. "sdb:mda+sdb:gdb"
sub StorePartTypeInfo{
my $info = shift;
my $disk;
for $disk(split(/\+/, $info)){
my ($name, $type) = split(/:/, $disk);
$s_wantedDiskType{$name} = $type;
}
}

# Builds all PV of a LVM
# @param pvlist a list of all PV partitions (which do not already exist)
# e.g. "sda1-9-2048-1000000+sdb1-9-2048-1000000"
sub BuildLVMs{
my $pvlist = shift;
my $pv;
for $pv (split(/\+/, $pvlist)){
my ($name, $from, $to) = split(/-/, $pv);
BuildPV($name, $from, $to, $MBR);
last if ($s_errors > 0);
}
}

# Builds one PV of a LVM
# @param name name of the partition, e.g. sda1
# @param from first record (of the disk)
# @param to last record
sub BuildPV{
my $name = shift;
my $from = shift;
my $to = shift;
my $partType = FindDiskType($name);
my $disk = FindDiskName($name);
my $no;
$name =~ /^\D+(\d+)/;
$no = $1;
if (! SectorsOverlap($disk, $no, $from, $to)){
if ($partType eq $MBR){
my $content = "n\n";
my $class = GetNewPartClass($disk);
if ($class eq "b"){
$content .= $no < 5 ? "p\n" : "l\n";
} else {
$content .= $class . "\n";
}
$content .= "$no\n$from\n$to\nt\n";
if (CountOfPartitions($disk) > 0){
$content .= "$no\n";
}
$content .= "8e\nw\n";
my $fn = WriteFile($content);
Exec("$s_fdisk /dev/$disk < $fn");
my $info = $s_diskInfo{$disk} = ReadFdiskInfo($disk);
if (index($info, ":$no-$from-$to") < 0){
Error("creating $name failed!");
} else {
Log("=== $name created");
}
} elsif ($partType eq $GPT){
my $content = "n\n$no\n$from\n$to\n8e00\nw\nY\n";
my $fn = WriteFile($content);
Exec("$s_gdisk /dev/$disk < $fn");
my $info = $s_diskInfo{$disk} = ReadGdiskInfo($disk);
if (index($info, ":$no-$from-$to") < 0){
Error("creating $name failed!");
} else {
Log("=== $name created");
}
} elsif ($partType eq "!"){
# error already is displayed
} else {
Error("Unknown partType: $partType");
}
}
Exec("partprobe");
}

# Counts the number of partitions of the disk.
# @param disk e.g. sdb
# @return the number of partitions of the disk
sub CountOfPartitions{
my $disk = shift;
my $info = $s_diskInfo{$disk};
my $rc = 0;
if ($info =~ /part(:\S+)/){
my $info = $1;
# Count the ':' in $info:
$rc = $info =~ tr/:/:/;
}
return $rc;
}

# Checks whether a partition already exists or whether the
# sectors of the new partition overlap of an existing partition
# @param disk e.g. sdb
# @param partNo the number of the new partition
# @param from the first sector of the new partition
# @param to the last sector of the new partition
# @return 1: error 0: OK
sub SectorsOverlap{
my $disk = shift;
my $partNo = shift;
my $from = shift;
my $to = shift;
my $info = GetDiskInfo($disk);
my $rc = 0;
if ($info =~ /:$partNo-/){
Error("partition $partNo already exists");
$rc = 1;
} else {
if ($info =~ /part:(\S+)/){
my $parts = $1;
my @parts = split(/:/, $parts);
foreach(@parts){
my ($no, $f, $t) = split(/-/);
if ($info !~ /ext:$no/
&& ($f >= $from && $f <= $to || $t >= $from && $t <= $to)){
&Error("partition $no overlaps with $partNo: $f-$t / $from-$to");
$rc = 1;
}
}
}
}
return $rc;
}
# Finds the disk name for a given partition
# @param part partition name, e.g. "sda1"
# @return the disk name, e.g. "sda"
sub FindDiskName{
my $part = shift;
my $rc;
if ($part =~ /^([a-z]+)/){
$rc = $1;
}
return $rc;
}

# Finds the partition table type.
# If the disk has no type (uninitialized) it will create a partition table
# @param part e.g. "sda1"
# @return "" (undef), "mbr" or "gpt"
sub FindDiskType{
my $part = shift;
my $disk = FindDiskName($part);
my $rc;
if ($s_realDiskType{$disk} ne ""){
$rc = $s_realDiskType{$disk};
} else {
# gdisk waits for an answer if the GDP/MBR is damaged
my $input = WriteFile("1\n");
my $answer = WriteFile("<none>");
Exec("$s_gdisk -l /dev/$disk < $input >$answer");
open(EXEC, $answer) || Error ("$answer: $!");
my @lines = <EXEC>;
close EXEC;
unlink $input;
unlink $answer;
my $mbrOnly;
foreach(@lines){
# "MBR: protective" will be ignored!
if (/MBR: (present|MBR only)/){
$rc = $MBR;
$mbrOnly = $1 eq "MBR only";
} elsif (/GPT: damaged/){
Error("GPT is damaged. Must be repaired manually.");
$rc = "!";
last;
} elsif (/GPT: present/){
if ($mbrOnly){
Error("GPT mixed with MBR. Fix it manually with gdisk, e.g: x (expert only) z (destroy GPT)");
$rc = "!";
} else {
$rc = $GPT;
}
last;
}
}
if ($rc eq ""){
$rc = $s_wantedDiskType{$disk};
if ($rc eq ""){
Error("No partition table type given for $disk: mbr will be taken");
$rc = $MBR;
}
CreatePartitionTable($disk, $rc);
}
$s_realDiskType{$disk} = $rc;
}
return $rc;
}

# Creates a partition table for a disk.
# @param disk disk name, e.g. sdc
# @param type partition table type: "mbr" or "gpt"
sub CreatePartitionTable{
my $disk = shift;
my $type = shift;
if ($s_allowInit ne "YES"){
Error("not allowed to create a partition table");
} elsif ($type eq $MBR){
my $fn = WriteFile("o\nw\n");
Exec("$s_fdisk /dev/$disk < $fn");
&Log("=== $disk initialized ($MBR)");
unlink $fn;
} else {
open(EXEC, "|$s_gdisk /dev/$disk");
print EXEC "o\n", "w\n", "Y\n";
close EXEC;
&Log("=== $disk initialized ($GPT)");
}
}

# Executes a command.
# @param cmd the command to execute
sub Exec{
my $cmd = shift;
my $extendedLog = shift;
if ($extendedLog){
Log("=== $cmd");
} else {
Log($cmd);
}
system($cmd);
}

# Writes a given content to a temporary file.
# @param content content to write
# @return filename
sub WriteFile{
my $content = shift;
my $suffix = shift;
my $fn = "/tmp/$$." . time() . ".tmp$suffix";
if ($content ne "<none>"){
open(OUT, ">$fn") || die "$fn: $!";
print OUT $content;
close OUT;
}
return $fn;
}
# Logs a message.
# @param msg message
sub Log{
my $msg = shift;
print $msg, "\n";
push(@s_logLines, $msg . "\n");
}

# Handles an error message.
# @param msg error message
sub Error{
my $msg = shift;
$s_errors++;
&Log("===+++ $msg");
}

# Writes the progress file.
#@param task name of the current task
sub Progress{
my $task = shift;
my $isLast = shift;
$task .= " ...";
$s_currTask++;
$s_maxTasks = $s_currTask if $isLast;
if ($s_currTask == $s_maxTasks){
$s_maxTasks += 5;
}
my $temp = $s_fnProgress . ".tmp";
open(PROGRESS, ">$temp") || die "$temp: $!";
my $percent = int(100 * ($s_currTask - 1) / $s_maxTasks);
$percent = 5 if $percent < 5;
print PROGRESS <<EOS;
PERC=$percent
CURRENT=<b>$task</b>
COMPLETE=completed $s_currTask of $s_maxTasks
EOS
close PROGRESS;
unlink $s_fnProgress if -f $s_fnProgress;
rename $temp, $s_fnProgress;
}

+ 22
- 0
backend/autopart.sh View File

@@ -0,0 +1,22 @@
#! /bin/bash
test -n "$VERBOSE" && set -x
ANSWER=$1
CMD=$2
DISKINFO=$3
ALLOW_INIT=$4
PARTS=$5
VG_INFO=$6
LV_INFO=$7
PROGRESS=$8
FULL_LOG=../public/autopart_log.txt

if [ -z "$VERBOSE" ] ; then
perl autopart.pl "$CMD" "$ANSWER" "$DISKINFO" "$ALLOW_INIT" "$PARTS" \
"$VG_INFO" "$LV_INFO" "$PROGRESS" > $FULL_LOG 2>&1
else
perl autopart.pl "$CMD" "$ANSWER" "$DISKINFO" "$ALLOW_INIT" "$PARTS" \
"$VG_INFO" "$LV_INFO" "$PROGRESS" 2>&1 | tee $FULL_LOG
fi
test -n "$VERBOSE" && cat "$ANSWER"
test -n "$VERBOSE" && ls -ld $FULL_LOG
test -n "$VERBOSE" && echo "=================================================="

+ 26
- 0
bin/sidu-installer-control View File

@@ -0,0 +1,26 @@
#! /bin/bash

CMD=$1
test -n "$CMD" || CMD=start

case "$1" in
start)
pywwetha-control $CMD
sidu-base-control $CMD
if [ -n "${DISPLAY}" ] ; then
# Launches x-www-browser if we are in kde, xfce, lxde, rqt, gnome
x-www-browser http://sidu-installer:8086
else
# Launches www-browser (elinks) if we are in nox
www-browser http://sidu-installer:8086
fi
;;
stop)
sidu-base-control $CMD
pywwetha-control $CMD
;;
*)
echo "unknown command! Use start stop."
exit 1
;;
esac

+ 230
- 0
config/sidu-installer_en.conf View File

@@ -0,0 +1,230 @@
# Sequence of the pages separated by a comma.
# The name of the page is the name of the plugin.
# The series define the effect of the back and next button.
# Lines, which must be translated, should be marked with %= (instead of =)
#global definitions:

.gui.text.prev=Back
.gui.text.next=Next

.txt_title=Siduction Installer

#Plugin specific:

home.txt_welcome=Welcome!
home.txt_introduction=This application will install siduction to your harddisk.
home.txt_intro2=Formating a partition can always result in data loss!
home.txt_warning=siduction takes no responsibility for data loss or hardware damage.
home.txt_warning2=Please be careful.
home.txt_help=If you need any help with the installation, visit the siduction support.
home.txt_chat=Chat
home.txt_channel=Channel
home.txt_forum=Forum
home.txt_wiki=Wiki
home.txt_usb_installer=Live USB-Stick Installer
home.txt_alt_usb=USB-Stick
home.txt_alt_warning=USB-Stick
home.txt_clear_config=Clear Configuration

diskinfo.txt_wait_for_partinfo%=The partition info will be established. Please be patient...
diskinfo.header_part_info%=Device;Label;Size;Part.type;FSys;Additional Info
diskinfo.created%=Created
diskinfo.modified%=Modified
diskinfo.opts_infostate=;No info;Partition info;Disk info
diskinfo.txt_infostate=Activate
diskinfo.txt_intro_info%=In this paragraph you can inform yourself about the existing partitions.
diskinfo.txt_disk%=Harddisk
diskinfo.txt_refresh%=Refresh
diskinfo.txt_exec%=Execute
diskinfo.txt_device%=Device
diskinfo.txt_label%=Label
diskinfo.txt_info%=Additional Info
diskinfo.txt_fs%=FSys
diskinfo.txt_size%=Size
diskinfo.txt_parttype%=Part.type

autopart.txt_title=Automatic Partitioning
autopart.txt_no_space1=There is no empty space on the disk(s). No automatic partitioning can be done.
autopart.txt_no_space2=If you want to do automatic partitioning you must add an empty disk or delete an unused partition on a existing disk.
autopart.txt_no_space3=If you want to install on already existing partitions please use the manual partitioning.
autopart.txt_intro1=There is empty space which allows to create automatically partitions for installation.
autopart.txt_button_manual=Manual partitioning
autopart.opts_template=,Standard (LVM),Encryption
autopart.txt_template=Proposal
autopart.txt_activate=Activate
autopart.txt_disk=Disk:
autopart.txt_partition=Partition:
autopart.txt_vg=Volume group (VG)
autopart.txt_size_boot=Size boot
autopart.txt_size_root=Size root
autopart.txt_size_swap=Size swap
autopart.txt_size_home=Size home
autopart.txt_sizefree=Not used/Total
autopart.txt_button_create=Create partitions
autopart.txt_button_check=Check
autopart.txt_intro_free_part=<xml>Here is the free space on the disk(s). Please select all that is going to be used for the automatic partitioning.<br/>The 2nd line describes the position of the space on the disk.
autopart.txt_too_much_space=The sum of the space for the partitions is larger than the available space!
autopart.txt_proposals=Recommendations
autopart.txt_std_hints=An empty field will be filled with the recommendation, a "*" sets all that is left over.
autopart.txt_std_descr=The free space is used for a volume group (VG). In this VG the various partitions then will be created.
autopart.err_missing_partition=No partition selected!
autopart.err_missing_vg=No name for the volume group set!
autopart.err_too_small=Too few space for an installation! Necessary: ${diskinfo.root.minsize.mb}MB
autopart.txt_successful=<xml>The automatic partitioning was <b>successful</b>.
autopart.txt_failed=<xml>Sorry, the automatic partitioning was <b>not successful</b>
autopart.txt_full_log=Full protocol
autopart.txt_button_retry=New attempt

partition.txt_title%=Changing the Partition Scheme
partition.txt_intro%=On this page you can create/change/delete the partitions of the harddisk(s).If not necessary click "next" (at the bottom right).
partition.txt_intro2%=To do this you must call an external program, which you can choose at "Part.Manager".
partition.txt_intro3%=Some programs need a harddisk on calling. In this case you must select it on "Harddisk".
partition.txt_intro4%=Please click "Execute" to start the program.
partition.txt_intro5%=<xml>You have more possibilities at <a href="http://sidu-disk-center:8086" target="_blank">sidu-disk-center</a>.
partition.txt_partman%=Part.manager
partition.err_all_not_allowed%=You must choose a disk. "all" is not allowed for this program.
partition.txt_disk%=Harddisk
partition.txt_reload%=Reload diskinfo scheme from disk
partition.txt_all%=all
partition.description_wait%=Please edit partitions and then close the external program.
partition.txt_button_auto=Automatic Partitioning

rootfs.txt_title%=The Root File System
rootfs.opts_filesys%=<xml>,no change,ext4,ext3,reiserfs,xfs,btrfs
rootfs.txt_intro%=<xml>Please <b>choose a partition</b>, where the root file system of siduction will be created.
rootfs.txt_intro2%=<xml><b>Note</b>:<br/><b>btrfs</b> is a <b>relative young</b> filesystem. We ship it, as we ship bleeding edge software. But a filesystem <b>can loose all your data</b>. So please <b>always backup your data</b> while testing btrfs. You have been <b>warned</b>, it might eat your kittens!
rootfs.txt_root%=Partition
rootfs.txt_filesys%=File system
rootfs.txt_intro_info%=Partition Info:
rootfs.txt_refresh%=Refresh
rootfs.txt_disk%=Harddisk
rootfs.txt_device%=Device
rootfs.txt_label%=Label
rootfs.txt_info%=Additional Info
rootfs.txt_fs%=FSys
rootfs.txt_size%=Size
rootfs.txt_parttype%=Part.type
rootfs.err_empty_root%=Please choose a partition for installation.
rootfs.txt_btrfs_warning%=<xml>The root filesystem is btrfs and it is lying on a GPT disk.<br/> With our experience <b>Grub can not boot</b> with this situation. Please create a small <b>boot partition</b> (200 MB) and format it with ext2..ext4.
rootfs.opts_confirmation%=;Stop! I will change the settings.;Continue! This is my decision!

mountpoint.txt_partition%=Mountpoint Definitions
mountpoint.txt_intro%=<xml>The access to <b>additional partitions</b> is done by <b>mounting</b>. In doing so a directory named <b>mountpoint</b> is defined as entry to this partition. It is located in the root filesystem normally.
mountpoint.txt_intro2%=<xml>Only partitions <b>with a filesystem</b> can be mounted.
mountpoint.txt_intro3%=<xml>We advise to use at least a <b>separate partition</b> for <b>/home</b>. This makes it simple to switch or reinstall the operating system.
mountpoint.txt_disk%=Harddisk
mountpoint.txt_device%=Device
mountpoint.txt_label%=Label
mountpoint.txt_mount%=Mount Point
mountpoint.txt_filesys%=File System
mountpoint.txt_add%=Add
mountpoint.txt_switch%=The selection criteria can be switched:
mountpoint.txt_action%=Action
mountpoint.txt_button_del%=Remove
mountpoint.err_is_root%=You may not choose the root directory (see previous page)!
mountpoint.err_wrong_mount%=No valid mountpoint given: Must start with '/'.
mountpoint.txt_intro_info%=These are the current partitions:
mountpoint.txt_refresh%=Refresh
mountpoint.txt_device%=Device
mountpoint.txt_fs%=FSys
mountpoint.txt_label%=Label
mountpoint.txt_info%=Additional Info
mountpoint.txt_size%=Size
mountpoint.txt_parttype%=Part.type
mountpoint.opts_mountonboot=;yes;no
mountpoint.txt_mount_on_boot%=Mount on boot?
mountpoint.txt_desc_mount_on_boot%=
mountpoint.txt_button_DEVICE%=Devices
mountpoint.txt_button_LABEL%=Labels
mountpoint.txt_button_POINT_COMBO%=Proposals
mountpoint.txt_button_POINT_TEXT%=Free input

boot.txt_bootloader%=Bootloader and Timezone
boot.txt_description%=A bootmanager allows you to choose the operating system you want to boot at startup.
boot.txt_manager%=Bootmanager
boot.txt_target%=Installation Target
boot.opts_loader%=;GRUB;None
boot.opts_target%=;Masterboot-Sector;Partition
boot.txt_region%=Region
boot.txt_city%=Stadt/Zone
boot.txt_refresh%=Refresh
boot.file.demo.currentzone=/etc/inosid/currentzone.txt
boot.file.demo.timezoneinfo=/etc/inosid/timezoneinfo.txt
boot.file.currentzone=${tempdir}currentzone.txt
boot.file.timezoneinfo=${tempdir}timezoneinfo.txt
boot.txt_desc_target_1%=<xml>Only <b>one disk</b> in the system? Use 1st entry
boot.txt_desc_target_2%=<xml>More than one disk? Choose your <b>bootdisk</b> (from 3rd entry down)
boot.txt_desc_target_3%="Partition" will install GRUB in the root partition. This will <b>not boot</b> without another boot-mananger present in a MBR.

user.txt_root_password=Root Password
user.txt_root_again=Repeat
user.txt_real_name=Real Name
user.txt_desc_root_password=This password is for the administrator.
user.txt_desc_password2=Please repeat the password.
user.txt_desc_password=At least 6, maximum 20 characters
user.txt_optional=Optional
user.txt_desc_login=Allowed: a..z, 0..9 and _

user.txt_user_data=User Settings
user.txt_login_name=Login Name
user.txt_password=Password
user.txt_again=Again
user.txt_show_passwd=Display the password?
user.opts_show_passwd=;no;yes

network.txt_network%=Network
network.txt_host%=Host Name
network.txt_desc_host%=Needs to start with a letter, letters or digits may be used after that.
network.txt_services%=Start Services:
network.txt_ssh%=Starts ssh
network.txt_descr_ssh%=Secure Sessions over Network.
network.opts_ssh%=;yes;no
network.default_host%=siductionbox

packet.txt_title%=Additional Packages
packet.txt_intro%=Debian wants to offer as much free software as possible. Unfortunatly some hardware will work only with unfree software which is restricted to the non-free repository. On the other hand there is software (contrib), which is free but depends on parts from non-free.
packet.txt_intro2%=This will never match with the DFSG.
packet.txt_contrib_installed%=The repositories non-free and contrib are listed as sources and can be used.
packet.txt_button_contrib%=Use non-free and contrib
packet.txt_firmware_intro%=Hardware was found, which needs unfree firmware. This can be done here.
packet.txt_firmware_intro2%=By installing this firmware, you are adding non-free sources to your system. It will not be compliant with DFSG anymore.
packet.txt_module%=Module:
packet.txt_statements%=Installation commands:
packet.txt_action%=Action:
packet.txt_button_install_all%=Install all Modules
packet.txt_found_firmware%=The following installed modules has been found:
packet.txt_button_install%=Install module
packet.txt_link_text%=Log of the last installation

run.txt_title%=Installation
run.txt_info%=You have provided all the required information for a siduction installation.
run.txt_tip%=To check again or change anything, press "Back".
run.txt_tip2%=If everything is correct, press "Save config and install".
run.txt_force%=Force Installation:
run.txt_descr_force%=Ignore RAM- Partition- and other checks.
run.opts_force%=;no;yes
run.txt_button_install%=Save config and install
run.info_running%=Installation has been finished. Duration: {{duration}} Please reboot now.
run.txt_rootfs%=Root File System
run.description_wait%=This will take a few minutes.
run.wait.intro%=The installation is running.
run.txt_reinstall=Repeat installation
run.txt_ready_title%=Installation Finished
run.txt_ready_intro%=The installation of siduduction was finished successfully. Please reboot the system.
run.txt_ready_failed%=Sorry, the installation of siduction <strong>has failed</strong>. Please remove the error and <a href="http:run">start again</a>.
run.txt_ready_runtime%=Runtime of the installation: {{duration}}
run.txt_ready_help%=Do you have problems? Surf to the IRC channel or to the forum.
run.txt_ready_reboot%=Reboot
run.txt_ready_details=Log

wait.txt_wait%=I am waiting...
wait.txt_intro%=The installation resumes, when the running application {{1}} is finished.
wait.txt_cancel%=Cancel
wait.txt_demotext%=Demo Mode: The program run will be simulated.
wait.refresh=3
wait.progress_state%=In work:

check.button_check_config=Check configuration
check.title=Checks
check.intro=This checks allow the maintainance of the application.
check.config.title=Results of the Configuration Test

BIN
data/config.db View File


+ 1
- 1
debian/changelog View File

@@ -1,4 +1,4 @@
:sidu-installer (2.0.1) experimental; urgency=low
sidu-installer (2.0.1) experimental; urgency=low

* renamed to sidu-installer
* progress bar while running autopart or partinfo

+ 4
- 0
import_config.sh View File

@@ -0,0 +1,4 @@
#! /bin/bash
python ../sidu-base/util/configurationbuilder.py -v --summary --drop-tables --prefix=sidu-base data/config.db ../sidu-base/config
python ../sidu-base/util/configurationbuilder.py -v --summary --prefix=sidu-installer data/config.db config


+ 33
- 0
installer/.coverage View File

@@ -0,0 +1,33 @@
€}q(U collectorqU coverage v3.4qUlinesq}q(U,/usr/share/pyshared/django/utils/timezone.pyq]q(KKKKK
K K K KKKKKKK!K#K&K)K,K/K5K7KBKEKKKQKTK]KcKeKvK|K~K„KŠK›K©K˛K˝KľKÂKČKŃKčKőKţMMeU./usr/share/pyshared/django/core/files/utils.pyq]q (KK K K K KKKKKKKKKKKKKKeU./usr/share/pyshared/django/utils/functional.pyq
]q (KKKK K
K KKKK K$K%K(K,K1K2K4K<KAKBKDKEKFKGKHKJKPKQKRKSKTKUKYKZK[K\K]K^K_K`KaKdKfKiKrKtKuKvKxK{K~KŠK’K™KśKžK KŁKŞK˛K´KµK¶K·K¸KşKĽKĂKÄKĹKÇKÉKĘKĚKŇKŮKŕKáKäKęKëK÷KúKűKýM MMMMMMMM-eU-/usr/share/pyshared/django/utils/timesince.pyq ]q (KKKKK7eU+/usr/share/pyshared/nose/plugins/manager.pyq]q(KSKTKXKYKZK[K^KdKeKfKjKmKqKsKtKvKwKxK{K�K„K…K†K‡K�K‰KŠK‹K�KˇK˘KŁK¬K­KłKńKňKóKôKőKöeU2/usr/share/pyshared/django/utils/datastructures.pyq]q(KKKK K KKKK K&K0K4K8K;K>KAKGKHKJKNKXKcKfKgKlKK�K�KŚKŹK�KťK K¤K§KŞK­K±KµKşKľKČKĎKÖKÚKŰKÝKňKóKöKúMM MMM M&M3M?MBMHMPMTM[McMgMkMoMtMxMŤM“M§M¨M´MżMÁMËMŇMÓMÔMŐMÖM×MŘMŮMÚMŰMÜMÝMŢMŕMčMéMîeU"/usr/share/pyshared/nose/loader.pyq]q(KOKQKRKSKTKUKVKYKZK[K\K]K^K_K`MVMXM[M\M_M`MxMyMzM}M~M„M…M†M�M‰MŠMŚMŤMŽMŹM˝MľMżMŔMĹeU)/usr/share/pyshared/django/utils/dates.pyq]q(KKKKKK K
K K KKKKKKKKKKKKKKK K!K"K#K$K%K&K'K(K)K*K,K-K.K/K0K1K2K3K4K5K6K7K8eU+/usr/share/pyshared/nose/plugins/capture.pyq]q(K9K:KDKIKJKKKOKmKnKoKrKsK|K}eU(/usr/share/pyshared/django/utils/text.pyq]q(KKKKKKKK K KKKKKKK3K6K9K:K=KMKrKtK~K€KŚKĎKÔKÖKŰKÝKčKęKýK˙MMM M MMMM!M2M6M>M@MRMTMfMhMjMlM~eU./usr/share/pyshared/django/template/context.pyq]q(KKKKK
K K KKKKKKKK#K(K-K1K8K<KBKEKKKTKUKVKWK_KdKkKyKzK~K�K‰K K¦K§K¨eU2/usr/share/pyshared/django/http/multipartparser.pyq]q(KKK K
K K K KKKKKKKKKKKK%K&K\KěKúKţMMMMM0MAMJMMMZMmMrMsMwM�M„M‡M�MŚMŹM•MźMˇMµM¸MáMřM
MDMEMIMOM_eU+/home/ws/py/sidu-manual/website/__init__.pyq]qKaU./usr/share/pyshared/nose/plugins/logcapture.pyq ]q!(KSK©KŞK¸KąKşK»K˝KĎKÔKßKŕKáKćKçeU//usr/share/pyshared/django/template/__init__.pyq"]q#(K2K5K=KBKHKLKPeU*/usr/share/pyshared/django/utils/crypto.pyq$]q%(KKKKKK K
K KKKKKKKK4K5KNK\KeKmK|eU+/usr/share/pyshared/django/http/__init__.pyq&]q'(KKKKKKKK
K K K KKKKKKKKK!K#K$K%K'K(K*K-K?KAKBK[K\KdKnKoKsKtKuKvKwKxKyKzK{K|K~K€K�K„K…K‡KŠK‹KŚK·K¸KşK»KľKżKÁKČKËKŕKĺKüM
M MMM*M-M/M3M8M>M@MIMUMZM_M…M‰MŤM“M•M�MźM˘MŁMĄMŻM´M·MąM˝MĂMÇMÍMŐMŰMáMçMňMöMúMţMMM"M3M4M6M7M9M;M<MNMTMcMgMmMpMwM{MM�M„M‡M�M±MµMąMľMĆMČMĚMŇMŘMÝMŕMĺMćMčMďMđMňMóMőMöMřMůMűMüMţM˙MMMMM M M MMeU+/usr/share/pyshared/django/template/base.pyq(]q)(KKKKKKK
K K KKKKKKKKKKKKKKKK K$K%K&K'K(K)K*K+K,K-K.K/K1K6K:K;K<K=K@KBKFKHKIKKKLKNKOKQKSKWKZK^K_KaKbKeKhKkKlKpKsKtKuK€K…K�K�K›KśK˘K§K¶K·KĽKČKâKăKęMMMM"M.M1M4M7M:M=MCMFMIMLMOMRMVM\MbMjMkMqMwM}M†M™MŢMßMŕMáMâMäMńMňMóMôMőMöMůMűM M M8M`M€M‚M…MŽMźMˇMÍMÜMßMâMMMMMM M.M1M3M=MDMGMHMKMOMRMaMbMeMhMsMuM¦MéMóMřMúM˙MMM M
M M MMMMMMMMMM!M"M#M%M&M(M)M*M,M-M/M2M3M4M6M8M9M:M;M=M@MAMBMGMHMIMKMhM’MżMĎMÖM×MâMăMéMëMM M!M$M%eU./usr/share/pyshared/django/template/smartif.pyq*]q+(KK KKKKKKK K&K+K0K1K3K8KAKDKIKJKLKQKWK_K`KaKbKcKdKeKfKgKhKiKjKkKoKpKsKvKzK{K}K€K�K†K‰KŤKŽK�K“K–K—K™KŞK˛KşKÂKĚeU-/usr/share/pyshared/django/core/mail/utils.pyq,]q-(KKK
K KKeU4/usr/share/pyshared/django/core/handlers/__init__.pyq.]q/KaU0/usr/share/pyshared/django/utils/numberformat.pyq0]q1(KKKKeU /usr/share/pyshared/nose/core.pyq2]q3(K"K$K%K)K*K+K,K2K3K7K8K;K<K=K>K?K±K˛K·K˝KľKżKŔKÁKÂKĂKĹeU8/usr/share/pyshared/django/utils/translation/__init__.pyq4]q5(KKKKKK K KKKKKKKKKK!K,K.KEKHKJKMKOKRKUKXK[K^KaKbKcKdKeKfKhKkKnKoKtKzK€K�K†K‰KŚKŹK’K•K�KžK eU//usr/share/pyshared/django/core/urlresolvers.pyq6]q7(KK
K K KKKKKKKKKKKKK"K%K&K8K<K@KCKGKHKJKLKNKpKrKwKyK€K‚K‹K�K‘K™KŞK«K¸K»KĂKÓKŰKÜKęKíMMMM%M>MFMOMXM[M^MaMdMŽM”M•M™MˇM¦MŢMŕMčMđMřMM
eU0/usr/share/pyshared/django/core/handlers/base.pyq8]q9(KKKKKKK K KKKKKKKHKÄKćKđeU./usr/share/pyshared/django/contrib/__init__.pyq:]q;KaU//usr/share/pyshared/django/dispatch/__init__.pyq<]q=(KK eU./usr/share/pyshared/nose/plugins/errorclass.pyq>]q?(KŚKŤKŹK�K”K–K—K�K™KšeU#/usr/share/pyshared/nose/failure.pyq@]qA(KKKKKKKKK$K%eU0/usr/share/pyshared/django/core/mail/__init__.pyqB]qC(KKKKK KKK-K.K@KAKVKWKcKdeU+/usr/share/pyshared/django/utils/formats.pyqD]qE(KKKKKKKK K
KKKKK0K;K[K]KgKpK„K™K­eU*/usr/share/pyshared/django/core/signals.pyqF]qG(KKKKeU./usr/share/pyshared/django/dispatch/saferef.pyqH]qI(KKK K K%KEKGKIK`K�KŹK‘K™K›KźKĄK·KÇKČKŰKóeU(/usr/share/pyshared/django/utils/html.pyqJ]qK(KKKKKKK K
K KKKKKKKKKKKKKKK K%K(K)K*K+K,K-K.K/K0K1K2K6K7K9K>K@KIKRKTKWKYK\K^KaKcKfKhK{KÄKĆKćeU7/usr/share/pyshared/django/utils/simplejson/__init__.pyqL]qM(KcKiKkKlKoKvKwKyKzK{K€K‚K„K…K†K‰eU*/usr/share/pyshared/django/core/signing.pyqN]qO(K"K#K$K%K'K(K)K*K+K,K/K2K3K6K9K:K=KAKFKJKZK^K_KbKfK…K–K—KťK K¤K®K°KłK·eU&/usr/share/pyshared/django/__init__.pyqP]qQ(KKKKKKKKKKKKeU'/usr/share/pyshared/coverage/control.pyqR]qSMeaU-/usr/share/pyshared/django/utils/importlib.pyqT]qU(KKKKK#K$eU$/usr/share/pyshared/nose/selector.pyqV]qW(K#K%K(K)K*K+K,K-KŢKŕKáKâKăKäKĺKčKéKęKëKěKíKîKďKđKńKôeU0/usr/share/pyshared/django/utils/regex_helper.pyqX]qY(KK K KKKKKKKKKKKK K#K%K(K*KĹKŮKíMMeU+/usr/share/pyshared/django/core/__init__.pyqZ]q[KaU+/usr/share/pyshared/django/conf/__init__.pyq\]q](KK K
K K KKKKKKKK!K"K%K(K,K9KAKDKEKRKSKŠKŤK�K’K™KśK K˘K¦eU /usr/share/pyshared/nose/util.pyq^]q_(K�K§K¨KÁKÄKĹKĆKÇKČKĚMMMMMMMMMMMMM M;M<M=M>M?M@MAMBMGMHMIMJMKMLMZM[M\M]M_M`MĆMÇMČMÉMÝMŢMćMčMęMëMěeU2/usr/share/pyshared/django/template/defaulttags.pyq`]qa(KKKKKKK KKKKKKKKKKK%K&K)K*K:K;K@KLKMKTKUKXK`KaKdKkKlKnKwK}K�KËKĚKÎKÔKęKëKíKňKőKüKţMMM M MMM"M(M7M=M>MCM^M_MbMcMfMjMkMnMrMsMtMuMvMwMxMyMzM}M€M�M„M‹MłM´MąMĚMÍMŐMŘMŕMďM÷MSMWMeM€M¨MMM&M.M/M3M6M9M:M<M@MCMšMĂMĺMM(MmM‹M¬MM&eU(/usr/share/pyshared/django/http/utils.pyqb]qc(KK KK#K?eU(/home/ws/py/sidu-manual/website/tests.pyqd]qe(KKK eU,/usr/share/pyshared/django/utils/baseconv.pyqf]qg(K&K(K)K*K+K,K-K/K0K2K3K4K5K8K;KAKGK^K_K`KaKbKceU*/usr/share/pyshared/django/utils/tzinfo.pyqh]qi(KKKKK KKKKKK"K%K.K/K0K5K8K;KAKGKNeU./usr/share/pyshared/django/utils/dateformat.pyqj]qk(K KKKKKKKKKKKKKK&K'K*K0K6K:KEKMKQKUKYK]KjKnKsKtKvK€K„K‹KŹK“KžK˘K¦K­K±KµKąK˝KÁKĹKÉKÍKÔKŘKĺKéKđK÷KűMMMM$M3M8eU,/usr/share/pyshared/django/utils/__init__.pyql]qmKaU1/usr/share/pyshared/django/core/files/__init__.pyqn]qoKaU)/usr/share/pyshared/django/views/debug.pyqp]qq(KKKKKKKK K K K KKKKKKK0K8KFKHK\K`KbKhKnKqKuKwK€K™KĆKÉKĘKŰMM!M'MOMxM™M¦MÁM›MŘM MTeU)/usr/share/pyshared/nose/plugins/cover.pyqr]qs(K@KFKĄK¦eU*/usr/share/pyshared/django/test/signals.pyqt]qu(KKeU,/usr/share/pyshared/django/views/__init__.pyqv]qwKaU//usr/share/pyshared/django/core/mail/message.pyqx]qy(KKKKKKKKK K
K K KKKKKKKK K&K@KAKBKCKDKEKFKGKHKIKJKKKOKcKuKwK{KKŤKŹK“K—KĄK¨K©KŞK«K­K®KÍKÓKëKňKúM MMM M/MCMHMIMKMLMMMYM_MbeU)/usr/share/pyshared/django/test/client.pyqz]q{(KKKKKKKKK K
KKKKKKKKeU"/usr/share/pyshared/nose/result.pyq|]q}(K&K'K(K)K+K,K;K<K=KAKEKKKLKMKQKRKXKYKZKaKbKgKhKiKjKmKnKžKźK¶K·KşK»eU!/usr/share/pyshared/nose/suite.pyq~]q(K4KCKNKOKPK]K^K_K`KaKfKgKhKjKpKqK“K”K•K–K�K™KšK›KśKťK¬K°KČKËKĚKĎKĐK×KŘKŮKßKáKâKăM M MMMMMMMMMM M!M"M#M$M(M)M,M-M.M/M3M4M5M7M8M9M:MBMCMHMIMJMPMQMRMSMTMUMXMYMZM[M\M]M^McMdMeMgMhMiMkMlMmMnMoMsMtMuM‰M‹MŚM�M‘M’M“M”M•M–MˇM˘MŁM¤MĄM¦M§MŞM˛MłM¸MşMĽM˝MÂMĂMÄMĹMČMĘMËMÍMÎMĐMŃMÖMŮMÚMŰMÜMÝMŢMßMŕMáMâMăMäMĺMMMMM M!M"M#M&M'M)eU$/usr/share/pyshared/nose/importer.pyq€]q�(KKK K!K"K$K%K'K-K.K3K6K7K9K:K;K<K>K?K@KBKCKDKEKFKGKHKIKVKXKYKZK[K]K^K_K‚K�K…K†K‡K�K‰KŠK‹KŚKŤKŽKŹK�K‘K”eU2/usr/share/pyshared/django/utils/module_loading.pyq‚]q�(KKKKeU!/usr/share/pyshared/nose/proxy.pyq„]q…(KK+K-K.K/K9K:K;K<K>K@KNKPKQKRKSKYKcKiKjKkKqKrKsKwKxKyK}K~KK€K�K„K…K†K‡K�K«K¬K­KłK´KµeU2/usr/share/pyshared/django/conf/global_settings.pyq†]q‡(KK K KKKKK$K'K+K0K1K2K3K4K5K6K7K8K9K:K;K<K=K>K?K@KAKBKCKDKEKFKGKHKIKJKKKLKMKNKOKPKQKRKSKTKUKVKWKXKYKZK[K\K]K^K_K`KaKbKcKdKeKfKgKhKiKjKkKlKmKnKoKpKqKrKsKtKuKvKzK~KK€K„K�KŤKŽK‘K”K—KšK›KśKˇK§KŞK­K°K±K˛KµK¸KżKÎKŇKÖKÚKÝKŕKăKďKńKőKúMM MMMMMM#M(M-M1M7M;M?MCMHMMMQMVMaMjM~M�M†M‰MŤM�M”M�M™MśMťM M˘M©M˛MŔMÉMĘMËMĚMÍMÎMĎMĐMŃMŇMŮMÝMŢMßMĺMéMďMńMóMőMřMM MMMMMM M*M0M1M2M3M4M5M8M9M:M;M<M?M@MAMBMCMJMQMXM_MbMheU-/usr/share/pyshared/django/core/files/temp.pyq�]q‰(K
K K KKKK6K8eU./usr/share/pyshared/django/utils/safestring.pyqŠ]q‹(KKK K
K KKKKKKKKKK K,K9K;K?K@KJKWKYKhKzK{K|K}eU6/usr/share/pyshared/django/core/files/uploadhandler.pyqŚ]qŤ(KKKK
K K K KKKKKKKKKK%K+K.K/K1K5K6K8K;K<K>KEKWKdKkKtK{K~KK‚K‰KŚK‘K”K–KˇK§K°KÂeU)/usr/share/pyshared/coverage/collector.pyqŽ]qŹ(KčKéKëKöK÷eU,/usr/share/pyshared/django/utils/encoding.pyq�]q‘(KKKKKKKK
K KKKKKK)K6KhK„KźKµK¶K·K˝KĂKÄKĹeU)/usr/share/pyshared/django/db/__init__.pyq’]q“(KKKKKK eU+/usr/share/pyshared/django/test/__init__.pyq”]q•(KKeU0/usr/share/pyshared/django/core/handlers/wsgi.pyq–]q—(KKKKKK K
K K K KKKKKKKKKKKKKKK K!K"K#K$K%K&K'K(K)K*K+K,K-K.K/K0K1K2K3K4K5K6K7K8K9K:K;K<K=K>K?K@KAKBKCKDKEKFKGKHKIKLKPKQKWK`KlK€K�KśKˇK¤K©KŻK˛K·KşKżKÂKÇKČKÉKĘKËKÎKĎKĐKŇeU5/usr/share/pyshared/django/template/defaultfilters.pyq�]q™(KKKKKKKK
K K K KKKKKKKKKKK$K0K2K3K;KBKCKLKMKRKSKXKYKeKfKgKhKjKkK˝KľKĂKÄKĹKÓKÔKŮKÚKäKĺKďK˙MMMMMM$M%M4M5M:M;M<MJMKMLMPMQMRM\M]MbMcMlMmMvMwM€M�M†M‡M–M—MžMźM¨M©MŞMłM´MµMŔMÁMČMŃMŇMÝMŢMçMňMýMMMMM#M+M0MEMFM M«MĂMÄMŇMÓMáMâMíMîMűMMM M M0MIMJMuMzeU./usr/share/pyshared/django/utils/itercompat.pyqš]q›(KKKK K KKKK%K*eU-/usr/share/pyshared/django/core/exceptions.pyqś]qť(KKKKK K
K K KKKKKKKKKKKKKK K!K"K$K%K&K(K)K*K+K?KHKMeU3/usr/share/pyshared/django/contrib/auth/__init__.pyqž]qź(KKKKKKKK
KK'K7KNK_eU2/usr/share/pyshared/django/contrib/auth/signals.pyq ]qˇ(KKKeU'/usr/share/pyshared/django/utils/log.pyq˘]qŁ(KKKKKK
K KKKKKKK"K'K)K-KMKWK]K^KaKgKheU5/usr/share/pyshared/django/core/files/uploadedfile.pyq¤]qĄ(KKKKK K K KKKKKKK#K'K*K7K9K<K=KEKKKUKXKYK]K`KcKgKlKoKpKuK€eU&/usr/share/pyshared/django/db/utils.pyq¦]q§(KKKKKK KKKKKK9K:K=K>KBKVKaKdKgKkKlKK€K�K’K“K•K˘eU-/usr/share/pyshared/django/core/files/base.pyq¨]q©(KKKKKK
K K KKKKK K#K-K0K2K4K6KGKSKhKkKnKvKyK|K}K‚K…K�K‹eU /usr/share/pyshared/nose/case.pyqŞ]q«(KK!K"K$K%K&K'K(K)K*K-K0K1K3K;K<K@KEKFKJKcKdKeKfKgKhK€K�K‚K�K„K…KŚK“K”K•K—KšK›KźK KˇK˘KŁK¦K§K¨K¬K­K˛KłKąeU1/usr/share/pyshared/django/dispatch/dispatcher.pyq¬]q­(KKKKKK KKKKK K!K"K$KpK•K°KÖKëMeU(/usr/share/pyshared/django/utils/http.pyq®]qŻ(KKKKKKKK K
K K KKKKKKKKKKKK"K$K,K.K4K6K<K>KNK[KiKŤK�K¨KżKĚKŇKÓKäeU1/usr/share/pyshared/django/utils/datetime_safe.pyq°]q±(K
K K KKKKKKKK K+K-K9euu.

+ 0
- 0
installer/__init__.py View File


+ 21
- 0
installer/models.py View File

@@ -0,0 +1,21 @@
from django.db import models

# Create your models here.
class HttpSession(models.Model):
name = models.CharField(max_length=64)
language = models.CharField(max_length=8)
def __unicode__(self):
return self.name

class UserData(models.Model):
key = models.CharField(max_length=128)
value = models.TextField()
session = models.ForeignKey(HttpSession, verbose_name="Session")
def __unicode__(self):
return self.key
class Search(models.Model):
query = models.TextField()
def __unicode__(self):
return self.name[0:30] + '...' if len(self.name) > 33 else self.name

BIN
installer/static/favicon.ico View File


BIN
installer/static/images/header-logo.png View File


BIN
installer/static/images/header.png View File


+ 4
- 0
installer/static/images/index.html View File

@@ -0,0 +1,4 @@
<html>
<body><p>No accesss!</p>
</body>
</html>

BIN
installer/static/images/logo.png View File


BIN
installer/static/images/usb_stick.png View File


BIN
installer/static/images/usbstick.png View File


BIN
installer/static/images/warning.png View File


+ 165
- 0
installer/static/siduction.css View File

@@ -0,0 +1,165 @@
@CHARSET "UTF-8";
a:link {
color: blue;
text-decoration: none;
border-bottom: 1px dotted #ff4500;
}

a:visited {
color: purple;
text-decoration: none;
border-bottom: 1px dotted #ff4500;
}


a:focus {
color: #ff4500;
background-color: #eeeeee;
}

a:hover {
color: #ff4500;
background-color: #eeeeee;
}

a:active {
color: #ff4500;
}

body {
margin-left: auto;
margin-right: auto;
font-size: 13px;
font-family: 'DroidSansRegular', sans-serif;
color: #222222;
background-color: #e7e7e7;
}
input { font-size: 13px; }
select, option { font-size: 13px; font-weight: normal; }
h1, h2, h3 {
font-size: 20px;
font-family: 'DroidSansBold', sans-serif;
letter-spacing: 1px;
margin: 15px 0 0 0;
}
#main_area{
margin-left: auto;
margin-right: auto;
max-width: 670px;
}
#header {
margin-left: auto;
margin-right: auto;
width:99.9%;
background-repeat:no-repeat;
}
#content{
margin-top:-0.9%;
margin-left: auto;
margin-right: auto;
background: #ffffff;
border: 2px #535d6c solid;
border-bottom-right-radius:5px;
border-bottom-left-radius:5px;
max-width:94.2%;
}
#content h1{
text-align: center;
color: #ff6600;
font-size: 2em;
}

#text_area{
height: 75%;
}
.error_message{
color: red;
}
.center{
text-align: center;
}
.left_block{
float: left;
display: block;
margin-right: 1em;
}
.right_block{
float: right;
display: block;
}
.navigation{
padding-bottom:3%;
padding-left:5%;
padding-right:5%;
}
.next_button{
float: right;
}
input.main_button[type=submit]{ background: #ff6600; font-weight: bold; }
input[type=submit] { background: #cccccc; font-weight: bold; }
option[selected=selected] { background: #cccccc; font-weight: bold; }
.navigation input { background: #ff6600; font-weight: bold; }
.prev_button{
float: left;
}
.logo{
width:100%;
}
.button_one_only{
text-align: right;
}
.textblock{
padding: 0.7em;
margin: 0.5em;
border: 1px #ff6600 solid;
float: none;
border-radius:3px;
}
.warning{
font-weight: bold;
}
.col_label{
width: 10em;
}
table { empty-cells: show; }
th { text-align: left; font-weight: bold; }
table.table_col5 tr th { width: 20%; }
table.table_col5 tr td { width: 20%; }
table.table_col5 { width: 100%; }
td { vertical-align: top; }
.horicontal_line { border-top: 1px #ff6600 solid; margin-top: 1em; margin-bottom: 1em;}

#table_partition_mount, #table_partition_partman, #table_partition_root{
width: 100%;
}
/* ------ Plugin home ------- */
.home_img_warning {
margin-right: 1em;
float: left;
}
.home_img_usb {
margin-right: 1%;
}
.home_button_usb {
}
/* ----- Plugin wait ------- */
.wait_progress_frame{
width: 100%;
background-color: gray;
margin: 0;
border: black 1px solid;
}
.wait_progress{
background-color: #ff6600;
text-align: center;
color: black;
}
.progress_state{
text-align: left;
color: blue;
margin-top: 1em;
}
.unused{
border: 1px red solid;
}
.hidden { display: none; }

+ 25
- 0
installer/static/siduction.js View File

@@ -0,0 +1,25 @@
var s_nextField = "";

// Sets the focus to the previous defined field.
// This routine must be called by timeout().
// Therefore a global variable must be used instead of a method parameter.
function nextField(){
document.getElementsByName(s_nextField)[0].focus();
}

// Click a button and set the focus to a given field.
// @param button: name of the button to click
// @param field: name of the field which gets the focus
function clickAndSet(button, field){
s_nextField = field;
window.setTimeout(nextField, 200);
window.setTimeout(nextField, 1000);
document.getElementsByName(button)[0].click();
}
// Clicks a button
// @param button: name of the button to click
function autoClick(button){
document.getElementsByName(button)[0].click();
}

+ 73
- 0
installer/tests.py View File

@@ -0,0 +1,73 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".

Replace this with more appropriate tests for your application.
"""

import os.path
from django.test import TestCase
from website.session import Session
from util.util import Util
from util.configurationbuilder import ConfigurationBuilder, SqLiteConfigurationDb

class DummyRequest:
def __init__(self):
self.META = {}
self.META['HTTP_ACCEPT_LANGUAGE'] = 'de'
class SimpleTest(TestCase):
def setUp(self):
fn = Util.getTempFile('config.db', 'sidu-help')
if not os.path.exists(fn):
self._db = SqLiteConfigurationDb(fn)
self._db.buildConfig()
config = ConfigurationBuilder(self._db)
dirBase = '..' + os.sep + '..' + os.sep + 'sidu_base' + os.sep + 'config'
config.addDirectory(dirBase, 'sidu-base', '.conf')
dirHelp = '..' + os.sep + '..' + os.sep + 'sidu_help' + os.sep + 'config'
config.addDirectory(dirHelp, 'sidu-help', '.conf')
def getRequest(self):
return DummyRequest()
def getSession(self):
return Session(self.getRequest(), 'sidu-help')
def test0Start(self):
self.setUp()
def testSessionGetNameOfStaticFile(self):
session = self.getSession()
self.assertEquals('/home/ws/py/sidu_help/website/static/de/home-de.htm',
session.getNameOfStaticFile('home', 'de'))
self.assertEquals('/home/ws/py/sidu_help/website/static/en/home-en.htm',
session.getNameOfStaticFile('home', 'en'))
def testSessiontranslateInternalRefs(self):
session = self.getSession()
self.assertEquals('<a href="home">',
session.translateInternalRefs('<a href="home-de.htm">'))
self.assertEquals('<img src="/static/lib/images-de/installer-de/installer1-de.png">',
session.translateInternalRefs('<img src="../lib/images-de/installer-de/installer1-de.png">'))

def testGetBodyOfStatic(self):
session = self.getSession()
fn = Util.getTempFile('test_html.htm', 'sidu-help')
Util.writeFile(fn, '''
<html>
<body>
<div id="main-page">
<p>Text</p>
</div>
</body>
</html>
''' )
self.assertEquals("<p>Text</p>\n", session.getBodyOfStatic(fn))

def testGetTemplate(self):
session = self.getSession()
content = session.getTemplate('pageframe.html')
self.assertTrue(content.find('body') > 0)

+ 120
- 0
installer/views.py View File

@@ -0,0 +1,120 @@
# Create your views here.
from django.http import HttpResponse, HttpResponsePermanentRedirect
from isource.session import Session
from isource.homepage import HomePage
from isource.autopartpage import AutoPartPage
from isource.partitionpage import PartitionPage
from isource.rootfspage import RootFSPage
from isource.bootpage import BootPage
from isource.networkpage import NetworkPage
from isource.packetpage import PacketPage
from webbasic.waitpage import WaitPage
from isource.mountpointpage import MountpointPage
from isource.userpage import UserPage
from isource.globalpage import GlobalPage
from isource.runpage import RunPage
from isource.checkpage import CheckPage

def getSession(request):
homeDir = request.documentRoot if hasattr(request, "documentRoot") else None
session = Session(request, homeDir)
return session

def getFields(request):
fields = request.GET
if len(fields) < len(request.POST):
fields = request.POST
return fields
def handlePage(page, request, session):
page._globalPage = GlobalPage(session, request.COOKIES)
fields = getFields(request)
pageResult = page.handle('', fields, request.COOKIES)
if pageResult._body != None:
body = page.replaceInPageFrame(pageResult._body)
rc = HttpResponse(body)
else:
url = pageResult._url
session.trace('redirect to {:s} [{:s}]'.format(url, pageResult._caller))
absUrl = session.buildAbsUrl(url)
rc = HttpResponsePermanentRedirect(absUrl)
cookies = request.COOKIES
for cookie in cookies:
rc.set_cookie(cookie, session.unicodeToAscii(cookies[cookie]))
return rc
def index(request):
session = getSession(request)
absUrl = session.buildAbsUrl('/home')
rc = HttpResponsePermanentRedirect(absUrl)
return rc

def home(request):
session = getSession(request)
rc = handlePage(HomePage(session), request, session)
return rc

def autopart(request):
session = getSession(request)
rc = handlePage(AutoPartPage(session), request, session)
return rc

def partition(request):
session = getSession(request)
rc = handlePage(PartitionPage(session), request, session)
return rc

def rootfs(request):
session = getSession(request)
rc = handlePage(RootFSPage(session), request, session)
return rc

def mountpoint(request):
session = getSession(request)
rc = handlePage(MountpointPage(session), request, session)
return rc

def boot(request):
session = getSession(request)
rc = handlePage(BootPage(session), request, session)
return rc

def user(request):
session = getSession(request)
rc = handlePage(UserPage(session), request, session)
return rc

def network(request):
session = getSession(request)
rc = handlePage(NetworkPage(session), request, session)
return rc

def packet(request):
session = getSession(request)
rc = handlePage(PacketPage(session), request, session)
return rc

def wait(request):
session = getSession(request)
rc = handlePage(WaitPage(session), request, session)
return rc

def run(request):
session = getSession(request)
rc = handlePage(RunPage(session), request, session)
return rc

def root(request):
session = getSession(request)
absUrl = session.buildAbsUrl('/home')
rc = HttpResponsePermanentRedirect(absUrl)
return rc

def check(request):
session = getSession(request)
rc = handlePage(CheckPage(session), request, session)
return rc

#partition;rootfs;mountpoint;boot;user;network;packet;run

+ 0
- 0
isource/__init__.py View File


+ 432
- 0
isource/autopartpage.py View File

@@ -0,0 +1,432 @@
'''
Created on 10.03.2013

@author: hm
'''

import re, os.path
from webbasic.page import Page
from diskinfopage import DiskInfoPage
from util.util import Util
from basic.shellclient import SVOPT_DEFAULT

class AutoPartPage(Page):
'''
Handles the search page
'''


def __init__(self, session):
'''
Constructor.
@param session: the session info
'''
Page.__init__(self, "autopart", session)
self._diskInfo = DiskInfoPage(self)
self._freeSpaces = self._diskInfo._emptyPartitions
self._nodeFullLog = "public/autopart_log.txt"

def afterInit(self):
'''Will be called when the object is fully initialized.
Does some preloads: time consuming tasks will be done now,
while the user reads the introductions.
'''
self._disks = self._diskInfo.getDisksWithSpace()
def defineFields(self):
'''Defines the fields of the page.
This allows a generic handling of the fields.
'''
self.addField("template", None, 0)
self.addField("adisk", None, 0)
self.addField("apartition", None, 0)
self.addField("vg", "siduction")
self.addField("size_boot")
self.addField("size_root")
self.addField("size_swap")
self.addField("size_home")
for ix in xrange(len(self._freeSpaces)):
self.addField("part{:d}".format(ix), None, None, "b")
# Hidden fields:
self.addField("log")
self.addField("answer")
self.addField("progress")

def buildStandardLVM(self):
'''Builds the HTML code for the standard LVM template.
@return: the HTML code
'''
body = self._snippets.get("STD_LVM")
#body = body.replace("{{DISK}}", self._snippets.get("DISK"))
#body = body.replace("{{PARTITION}}", self._snippets.get("PARTITION"))
content = self._diskInfo.buildFreePartitionTable(None)
body = body.replace("{{PARTITIONS}}", content)
body = body.replace("{{ACTIVATE}}", self._snippets.get("ACTIVATE"))
body = body.replace("{{PARAMETER}}", self._snippets.get("PARAM_STD"))
sizeAvailable = self.calcAvailableSpace()
sizeUsed = self.getCorrectedUsedSpace()
sizeFree =self.humanReadableSize((sizeAvailable-sizeUsed)*1024)
sizeAvailable = self.humanReadableSize(sizeAvailable*1024)
body = body.replace("{{size_free}}", sizeFree)
body = body.replace("{{size_total}}", sizeAvailable)
return body

def buildReady(self, answer):
'''Builds the content if the automatic partitioning has been done.
@param answer: the file with the answer from the shell server
'''
content = self._snippets.get("READY")
url = self._session._urlStatic + self._nodeFullLog
content = content.replace("{{fullLog}}", url)
fileContent = self._session.readFile(answer, "===", True)
successful = fileContent.find("+++ ") < 0
if not successful:
message = self._session.getConfig("autopart.txt_failed")
else:
message = self._session.getConfig("autopart.txt_successful")
vg = self.getField("vg")
value = vg + "/root"
self._globalPage.putField("root", value)
self._session.putUserData("rootfs", "root", value)
self._globalPage.putField("rootfs", "-")
self._session.putUserData("rootfs", "filesys", "-")
mountPoints = ""
if self.getField("size_home") != "0M":
mountPoints = " /dev/" + vg + "/home:/home"
if self.getField("size_boot") != "0M":
mountPoints += " /dev/" + vg + "/boot:/boot"
self._globalPage.putField("mountpoints", mountPoints)
self._session.putUserData("mountpoint", "mounts", "-")
self._globalPage.putField("mountonboot", "yes")
content = content.replace("{{success}}", message)
content = content.replace("{{lines}}", fileContent)
content = content.replace("{{RETRY}}", "" if successful else self._snippets.get("RETRY"))
return content
def changeContent(self, body):
'''Changes the template in a customized way.
@param body: the HTML code of the page
@return: the modified body
'''
answer = self.getField("answer")
progress = self.getField("progress")
if progress != None and progress != "":
if os.path.exists(answer):
self.putField("progress", None)
content = self.buildReady(answer)
else:
content = self._snippets.get("WAIT")
message = self._session.getConfig("wait.txt_intro")
message = message.replace("{{1}}", "autopart")
content = content.replace("{{message}}", message)
bar = self._diskInfo.buildProgress(progress)
content = content.replace("{{PROGRESS}}", bar)
self.setRefresh()
elif not self._diskInfo._hasInfo:
content = self._snippets.get("WAIT")
message = self._session.getConfig("wait.txt_intro")
message = message.replace("{{1}}", "partinfo")
content = content.replace("{{message}}", message)
progress = self._diskInfo.buildProgress()
content = content.replace("{{PROGRESS}}", progress)
self.setRefresh()
else:
self._session.trace("changeContent(): answer: {:s}".format(answer))
if (answer != None and os.path.exists(answer)):
content = self.buildReady(answer)
elif len(self._freeSpaces) == 0:
content = self._snippets.get("NO_SPACE")
else:
templ = self.getField("template")
if templ == "std":
content2 = self.buildStandardLVM()
elif templ == "single":
content2 = ""
else:
content2 = self.buildStandardLVM()
content = self._snippets.get("ENOUGH_SPACE")
content = self.fillStaticSelected("template", content)
content = content.replace("{{TEMPLATE}}", content2)
body = body.replace("{{CONTENT}}", content)
return body

def calcAvailableSpace(self):
'''Calculates the sum of the selected free spaces:
@return: the size in kBytes
'''
size = 0
for ix in xrange(len(self._freeSpaces)):
val = self.getField("part{:d}".format(ix))
if val == "on":
info = self._freeSpaces[ix].split('-')
size += (int(info[2]) - int(info[1])) / 2
return size
def getCorrectedSizeValue(self, field):
'''Returns the value of a field containing a number
and optional a unit suffix.
If there is an error the field will be corrected:
If there is no number the field will be cleared.
If there is a wrong suffix it will be removed.
@param field: the name of the field
@return: the size in kByte
'''
val = self.getField(field)
size = 0
if val != None and val != "*" and len(val) != 0:
rexpr = re.compile(r'^(\d+)(.*)')
matcher = rexpr.match(val)
if matcher == None:
val = ""
self.putField(field, "")
else:
size = int(matcher.group(1))
suffix = matcher.group(2).upper()
unit = None
if suffix == "K":
unit = 1
elif suffix == "M":
unit = 1024
elif suffix == "G":
unit = 1024*1024
elif suffix == "T":
unit = 1024*1024*1024
else:
unit = 1024
suffix = "M"
self.putField(field, str(size) + suffix)
size *= unit
return size
def getCorrectedUsedSpace(self):
'''Returns the sum of the sizes of all specified partitions.
Wrong field values will be corrected. See getCorrectedSizeValue().
@return: the sum of the sizes of boot, root, swap and home
'''
size = self.getCorrectedSizeValue("size_boot")
size += self.getCorrectedSizeValue("size_root")
size += self.getCorrectedSizeValue("size_home")
size += self.getCorrectedSizeValue("size_swap")
return size
def calcStandardLVM(self):
'''The field values will be tested for "*".
If given the field will set to a value that the total space is used.
'''
sizeUsed = self.getCorrectedUsedSpace()
sizeFree = str((self.calcAvailableSpace() - sizeUsed) / 1024) + "M"
if self.getField("size_boot") == "*":
self.putField("size_boot", sizeFree)
if self.getField("size_root") == "*":
self.putField("size_root", sizeFree)
if self.getField("size_swap") == "*":
self.putField("size_swap", sizeFree)
if self.getField("size_home") == "*":
self.putField("size_home", sizeFree)

def checkStandardLVM(self):
'''Checks the validity of the input fields for the standard LVM proposal.
'''
MByte=1024
GByte=1024*1024
sizeAvailable = self.calcAvailableSpace()
if sizeAvailable <= 4*GByte:
home = 0
swap = 200
root = sizeAvailable - swap - home
elif sizeAvailable <= 8*GByte:
# Min: 2709 Max: 4G
root = (2000*MByte + (sizeAvailable - 2000) * (4*GByte - 2000*MByte)
/ (8*GByte - 2000*MByte))
# Min: 409M Max: 819M
swap = 200 + (sizeAvailable - 200) / 10
# Min: 976 Max: 3276
home = sizeAvailable - root - swap
elif sizeAvailable <= 32*GByte:
# min: 4G max: 16G
root = sizeAvailable / 2;
# min: 822M max: 2.3G
swap = 310*MByte + sizeAvailable / 16
# min: 3274M max: 14026M
home = sizeAvailable - root - swap
else:
root = 16*GByte
swap = 2500*MByte
home = sizeAvailable - root - swap
self.getCorrectedUsedSpace()
if self.getField("size_boot") == "":
self.putField("size_boot", "0")
if self.getField("size_root") == "":
self.putField("size_root", str(root / 1024) + "M")
if self.getField("size_swap") == "":
self.putField("size_swap", str(swap / 1024) + "M")
if self.getField("size_home") == "":
self.putField("size_home", str(home / 1024) + "M")
# Now replace the star if it exists:
self.calcStandardLVM()
sizeUsed = self.getCorrectedUsedSpace()
if sizeAvailable - sizeUsed < 0:
self.putError(None, "autopart.txt_too_much_space")
def checkInput(self):
'''Checks the validity of the input fields and calculates some infos.
'''
templ = self.getField("template")
if templ == "std":
self.checkStandardLVM()
else:
self._session.error("unknown state: " + templ)
def getFlavour(self):
rc = "siduction"
with open("/etc/siduction-version", "r") as fp:
for line in fp:
cols = line.split(" ")
rc = cols[0] + cols[1]
break
if line.find("kde") > 0:
rc += "-kde"
elif line.find("xfce") > 0:
rc += "-xfce"
elif line.find("lxde") > 0:
rc += "-lxde"
fp.close()
return rc
def createStandardLVM(self):
'''Realizes the standard LVM proposal.
@return: None or the PageResult
'''
pageResult = None
answer = self._session._shellClient.buildFileName("ap", ".ready")
program = "autopart"
allowInit = "YES"
diskInfo = ""
partitions = ""
for ix in xrange(len(self._freeSpaces)):
part = self._freeSpaces[ix]
val = self.getField("part{:d}".format(ix))
if val == "on":
# e.g. "sdb!1-2048-888888"
disk = part.split("!")[0]
if disk != None and diskInfo.find(disk) < 0:
if diskInfo != "":
diskInfo += "+"
diskInfo += disk + ":mbr"
if partitions != "":
partitions += "+"
# e.g. "sdb!3-2048-100000"
partitions += part.replace("!", "")
availableSpace = self.calcAvailableSpace()
extensionSize = self._session.nextPowerOf2(availableSpace/2024)
vgInfo = self.getField("vg") + ":" + str(extensionSize) + "K"
lvInfo = ""
minSize = int(self._session.getConfigWithoutLanguage("diskinfo.root.minsize.mb"))
size = self.getCorrectedSizeValue("size_root") / 1024
error = False
flavour = self.getFlavour()
if diskInfo == "":
error = self.putError(None, "autopart.err_missing_partition")
elif self.calcAvailableSpace() / 1024 < minSize:
error = self.putError(None, "autopart.err_too_small")
elif (size < minSize):
error = self.putError(None, "autopart.err_too_small")
if vgInfo.startswith(":"):
error = self.putError(None, "autopart.err_missing_vg")
if not error:
lvInfo = "root:{:s}:{:d}M:ext4".format(flavour, size)
sizeHome = self.getCorrectedSizeValue("size_home") / 1024
sizeSwap = self.getCorrectedSizeValue("size_swap") / 1024
sumSize = size + sizeHome + sizeSwap
# 6 extension must be reserved:
allUsed = sumSize * 1024 > availableSpace - 6 * extensionSize
if sizeHome > 0:
if allUsed and sizeSwap == 0:
lvInfo += ";home:home:*:ext4"
else:
lvInfo += ";home:home:{:d}M:ext4".format(sizeHome)
if sizeSwap > 0:
if allUsed:
lvInfo += ";swap:swap:*:swap"
else:
lvInfo += ";swap:swap:{:d}:swap".format(sizeSwap)

progress = self._session._shellClient.buildFileName("ap", ".progress")

params = ["stdlvm", diskInfo, allowInit, partitions, vgInfo, lvInfo,
progress]
self.execute(answer, SVOPT_DEFAULT, program, params, 0)
intro = "wait.txt_intro"
description = None
self.putField("answer", answer)
self.putField("progress", progress)
self._session.trace("createStandardLVM(): answer set to" + answer)
pageResult = self._session.redirect("autopart", "createStdVG")
self.setRefresh()
return pageResult
def createPartitons(self):
'''Realizes the current proposal.
@return: None or the PageResult
'''
pageResult = None
templ = self.getField("template")
if templ == "std":
pageResult = self.createStandardLVM()
else:
self._session.error("unknown state: " + templ)
return pageResult
def handleButton(self, button):
'''Do the actions after a button has been pushed.
@param button: the name of the pushed button
@return: None: OK<br>
otherwise: a redirect info (PageResult)
'''
pageResult = None
if button == "button_manual":
self.addPage("mountpoint", "autopart")
self.addPage("rootfs", "mountpoint")
self.addPage("partition", "rootfs")
self.delPage("autopart")
pageResult = self._session.redirect("partition", "autopart.handleButton")
elif button == "button_cancel":
fname = self._diskInfo._filePartInfo
Util.writeFile(fname, "# created by the cancel button")
elif button == "button_retry":
answer = self.getField("answer")
self._session.deleteFile(answer)
elif button == "button_check":
self.checkInput()
elif button == "button_reload":
self._diskInfo.reload()
pageResult = self._session.redirect("autopart", "autopart-reload")
self.setRefresh()
elif button == "button_activate":
pass
elif button == "button_recalc":
self.putField("size_boot", "")
self.putField("size_root", "")
self.putField("size_swap", "")
self.putField("size_home", "")
self.checkStandardLVM()
pass
elif button == "button_run1" or button == "button_run2":
pageResult = self.createPartitons()
elif button == "button_prev":
pageResult = self._session.redirect(
self.neighbourOf(self._name, True),
"autopart.handleButton")
elif button == "button_next":
pageResult = self._session.redirect(
self.neighbourOf(self._name, False),
"autopart.handleButton")
else:
self.buttonError(button)
return pageResult

+ 144
- 0
isource/bootpage.py View File

@@ -0,0 +1,144 @@
'''
Created on 10.03.2013

@author: hm
'''

from webbasic.page import Page
from diskinfopage import DiskInfoPage
import os.path, re
from basic.shellclient import SVOPT_DEFAULT

class BootPage(Page):
'''
Handles the page allowing changing the partitions.
'''


def __init__(self, session):
'''
Constructor.
@param session: the session info
'''
Page.__init__(self, 'boot', session)
self._diskInfo = DiskInfoPage(self)
def afterInit(self):
'''Will be called after all initializations are done.
Note: self._globalPage will be set after the constructor.
'''
region = self.getField("region")
if region == None or region == "":
content = self._session.readFile("/etc/timezone")
rexpr = re.compile("^(\w+)/(\w+)")
matcher = rexpr.match(content)
if matcher != None:
region = matcher.group(1)
city = matcher.group(2)
self.putField("region", region)
self.putField("city", city)
def defineFields(self):
'''Defines the fields of the page.
This allows a generic handling of the fields.
'''
self.addField("loader", None, 0, "v")
self.addField("target", None, 0, "v")
self.addField("region")
self.addField("city")
# Hidden fields:
def readTimezones(self):
'''Reads the file with the timezone info.
@return: a tuple (regions, cities)
'''
name = self._session.getConfigWithoutLanguage(".dir.temp") + "timezoneinfo.txt"
cities = []
zones = []
if not os.path.exists(name):
# self.execute(name, options, command, params, timeout)
pass
else:
currentZone = self.getField("region")
if currentZone == None:
currentZone = "Europe"
with open(name, "r") as fp:
lastZone = None
for line in fp:
(zone, city) = line.rstrip().split("/")
if zone != lastZone:
zones.append(zone)
lastZone = zone
if zone == currentZone:
cities.append(city)
return (zones, cities)
def changeContent(self, body):
'''Changes the template in a customized way.
@param body: the HTML code of the page
@return: the modified body
'''
body = self.fillStaticSelected("loader", body)
targets = self._diskInfo.getRealDisks()
values = self.autoSplit(self._session.getConfig("boot.opts_target"))
targets = values + targets
body = self.fillDynamicSelected('target', targets, None, body)
(regions, cities) = self.readTimezones()
body = self.fillDynamicSelected('region', regions, None, body)
body = self.fillDynamicSelected('city', cities, None, body)
return body
def setTimeZone(self):
'''Sets the timezone.
'''
answer = self._session._shellClient.buildFileName("tz", ".ready")
params = ["set",
"{:s}/{:s}".format(self.getField("region"), self.getField("city"))]
options = SVOPT_DEFAULT
command = "timezoneinfo"
self.execute(answer, options, command, params, 0)
def handleButton(self, button):
'''Do the actions after a button has been pushed.
@param button: the name of the pushed button
@return: None: OK<br>
otherwise: a redirect info (PageResult)
'''
pageResult = None
if button == "button_refresh":
# Show same page again:
pass
elif button == 'button_prev':
pageResult = self._session.redirect(
self.neighbourOf(self._name, True),
'boot.handleButton')
elif button == 'button_next':
self.storeAsGlobal("loader", "loader")
target = self.getField("target")
ix = self.findIndexOfOptions("target")
if ix == 0:
target = "mbr"
elif ix == 1:
target = "partition"
else:
target = "/dev/" + target
self._globalPage.putField("target", target)
zone = self.getField("region")
city = self.getField("city")
if zone == None or city == None:
self.putError("region", "boot.err_no_timezone")
else:
self.setTimeZone()
# force to fetch timezone info from system:
self.putField("region", None)
self.putField("city", None)
pageResult = self._session.redirect(
self.neighbourOf(self._name, False),
'boot.handleButton')
else:
self.buttonError(button)
return pageResult

+ 82
- 0
isource/checkpage.py View File

@@ -0,0 +1,82 @@
'''
Created on 13.03.2013

@author: hm
'''
import operator

from webbasic.page import Page
from webbasic.configcheck import ConfigChecker

class CheckPage(Page):
'''
Allows the selection of the language.
'''
def __init__(self, session):

'''
Constructor.
@param session: the session info
'''
Page.__init__(self, 'check', session)
self._resultBody = None
self._resultTitle = None

def defineFields(self):
'''Defines the fields of the page.
This allows a generic handling of the fields.
'''
self.addField('language')

def checkConfig(self):
checker = ConfigChecker(self._session)
self._resultTitle = self._session.getConfig('check.config.title')
self._resultBody = checker.checkConfig(self.getField('language'),
self._snippets.get('KEY_DIFF'), self._snippets.get('KEY_SEPARATOR'))
def changeContent(self, body):
'''Changes the template in a customized way.
@param body: the HTML code of the page
@return: the modified body
'''
field = 'language'
curLanguage = self.getField(field)
values = []
for lang in self._session._supportedLanguages:
if lang != 'en':
values.append(lang)
if curLanguage == None:
ix = 0
else:
ix = operator.indexOf(values, curLanguage)
body = self.fillOpts(field, values, None, ix, body)
if self._resultTitle == None:
title = ''
result = ''
else:
title = self._snippets.get('RESULT_TITLE')
title = title.replace('{{title}}', self._resultTitle)
if self._resultBody == None:
result = self._snippets.get('EMPTY_BODY')
else:
result = self._resultBody
body = body.replace('{{RESULT_TITLE}}', title)
body = body.replace('{{RESULT}}', result)
return body
def handleButton(self, button):
'''Do the actions after a button has been pushed.
@param button: the name of the pushed button
@return: None: OK<br>
otherwise: a redirect info (PageResult)
'''
pageResult = None
if button == 'button_check_config':
self.checkConfig()
else:
self.buttonError(button)
return pageResult

+ 556
- 0
isource/diskinfopage.py View File

@@ -0,0 +1,556 @@
'''
Created on 31.03.2013

@author: hm
'''
import os.path, re, time
from webbasic.page import Page, PageException
from basic.shellclient import SVOPT_BACKGROUND
from util.util import Util

class PartitionInfo:
'''the info of one partition
'''
def __init__(self, parent, dev, label, size, size2, ptype, pinfo, fs, info):
'''Constructor.
@param parent: an instance of DiskInfo
'''
self._parent = parent
# e.g. sda1
self._device = dev
# the name for sorting:
matcher = re.search(r'(\d+)', dev)
if matcher != None:
no = matcher.group(1)
dev.replace(no, "{:3s}".format(no))
self._sortName = dev
# volume label
self._label = label
# partition type, e.g. 8e
</