summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-03-31 23:34:57 +0000
committerIgor Pashev <pashev.igor@gmail.com>2013-04-01 00:32:30 +0000
commitad96e366d10a8b5fc49794a646eac6c5b5b0a949 (patch)
tree23b3fe554035c86e8be7eaedcdf6bf1c20036289
parent6e6dea80e5c938494c71796c9d7bd977f5c21db9 (diff)
downloadlive-ad96e366d10a8b5fc49794a646eac6c5b5b0a949.tar.gz
New installer: use snack (newt); can configure disks
-rw-r--r--.gitignore1
-rwxr-xr-xinstall175
-rw-r--r--lib/hdd/__init__.py182
-rwxr-xr-xsetup98
4 files changed, 263 insertions, 193 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/install b/install
new file mode 100755
index 0000000..3aa1192
--- /dev/null
+++ b/install
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+
+from lib.hdd import HDD
+from snack import *
+from subprocess import Popen, PIPE, call
+from pprint import pprint
+import re
+import sys
+
+# Snack screen
+screen = None
+
+# List of disks installed on the system:
+hdds = []
+
+# Number of HDDs installed on the system:
+number_of_disks = 0
+
+# List of zpools found on the system:
+zpools = []
+
+def welcome():
+ return ButtonChoiceWindow(screen, "Welcome to Dyson installer",
+ '''
+Dyson is an operating system derived from Debian and based on Illumos core. \
+It uses Illumos unix kernel and libc, ZFS file system and SMF to manage system startup. \
+To learn more visit http://osdyson.org
+
+This installer will guide you through disk paritioning, filesystem creation, \
+installation of base system and minimal configuration.
+
+Please note, this system IS VERY EXPERIMENTAL. \
+You can LOOSE ALL YOUR DATA which this system can reach ;-)
+
+Whould you like to continue?''',
+ buttons=[('Yes', True), ('No', False)], width=70, help=None)
+
+def get_imported_zpools():
+ zpool_cmd = Popen(['zpool', 'list', '-H', '-o', 'name'], stdout=PIPE)
+ zpool_list = zpool_cmd.stdout.read().rstrip().split('\n')
+ return zpool_list
+
+def get_exported_zpools():
+ zpool_cmd = Popen("zpool import | awk '/pool:/ {print $2}'", shell=True, stdout=PIPE)
+ zpool_list = zpool_cmd.stdout.read().rstrip().split('\n')
+ return zpool_list
+
+def get_hdds():
+ hdds = []
+ pat = re.compile('\d+\.\s+(\S+)\s+<(\S+)\s*.+>')
+ format_cmd = Popen('format </dev/null', shell=True, stdout=PIPE)
+ for line in format_cmd.stdout:
+ m = pat.search(line)
+ if m:
+ name = m.group(1)
+ desc = m.group(2)
+ hdds.append(HDD(name, desc))
+
+ return hdds
+
+
+def configure_hdd():
+ hdd_items = map(lambda x: '{}: {} {}'.format(x.name, x.capacity, x.description), hdds);
+ choice = None
+
+ while not choice in ['ok', 'cancel']:
+ (choice, hdd) = ListboxChoiceWindow(screen,
+ title='Choose hard disk drive',
+ text='Choose a hard drive for the root ZFS pool',
+ items=hdd_items,
+ buttons=[
+ ('Use selected disk', 'ok'),
+ ('Cancel', 'cancel')
+ ],
+ width=76,
+ scroll=1,
+ default=0,
+ help=None
+ )
+ if choice in ['ok', None]:
+ choice = configure_partitions(hdds[hdd], number_of_disks)
+ if choice == 'another':
+ choice = None
+
+def configure_partitions(hdd, number_of_disks = 1):
+ choice = None
+ top_text = 'For the root ZFS pool you need a disk containing exactly one solaris partition (id is 0xbf). \
+If this disk includes such a partition and you are happy with it, use this disk. \
+Otherwise you have to change partitioning. \
+'
+
+ while not choice in ['ok', 'another', 'cancel']:
+ part_info = top_text
+ part_info += '\n\n'
+
+ have_partitions = len(hdd.partitions) > 0
+ have_solaris_partition = False
+ buttons = []
+
+ if have_partitions:
+ for p in hdd.partitions:
+ part_info += '{capacity:10} {system} ({id}) - {primary}\n'.format(
+ capacity=p.capacity,
+ system=p.system,
+ id=hex(p.id),
+ primary=['primary', 'logical'][p.logical],
+ )
+ if p.id == 0xBF:
+ have_solaris_partition = True
+ else:
+ part_info += 'This disk is not partitioned.\n'
+
+ if have_solaris_partition:
+ buttons.append(('Use this disk', 'ok'))
+
+ buttons.append(('Edit partitions', 'fdisk'))
+
+ if number_of_disks > 1:
+ buttons.append(('Back', 'another'))
+
+ buttons.append(('Cancel', 'cancel'))
+
+ choice = ButtonChoiceWindow(screen,
+ title='Partitions of {}'.format(hdd.name),
+ text=part_info,
+ buttons=buttons,
+ width=76,
+ help=None
+ )
+ if choice == 'fdisk':
+ screen.suspend()
+ call(['cfdisk', hdd.raw_device])
+ hdd.reread_partitions()
+ screen.resume()
+ # end while
+
+ return choice
+
+
+class Abort(Exception):
+ pass
+class NoDisks(Exception):
+ pass
+
+
+# Begin installation:
+print("Getting list of hard disk drives ... ")
+hdds = get_hdds()
+number_of_disks = len(hdds)
+
+
+screen = SnackScreen()
+try:
+ screen.pushHelpLine(' ')
+
+ if number_of_disks == 0:
+ raise NoDisks('No disks found')
+
+ if not welcome():
+ raise Abort('Installation canceled')
+
+ zpools = []
+ zpools.append(get_imported_zpools())
+ zpools.append(get_exported_zpools())
+
+ configure_hdd()
+except Abort as e:
+ print (e)
+except NoDisks as e:
+ print (e)
+finally:
+ screen.finish()
+
+sys.exit(0)
+
diff --git a/lib/hdd/__init__.py b/lib/hdd/__init__.py
index 8c63933..6189e24 100644
--- a/lib/hdd/__init__.py
+++ b/lib/hdd/__init__.py
@@ -1,116 +1,108 @@
#!/usr/bin/python
-import subprocess
-import sys
+from subprocess import Popen, PIPE
import re
-import pprint
+import sys
-def humanCapasity(b):
+def human_capacity(b):
b = float(b)
- G = ['%.0f B', '%.2f KB', '%.2f MB', '%.2f GB', '%.2f TB']
+ G = ['%.0f B', '%.2f KiB', '%.2f MiB', '%.2f GiB', '%.2f TiB']
for p in G:
if b < 1024:
return p % b
else:
b /= 1024
- raise 'Too large hard drive ;-)'
-
+ raise Exception('Too large hard drive ;-)')
+
+class Partition(object):
+ blocks = 0
+ boot = False
+ bytes = 0
+ capacity = None
+ end = 0
+ id = 0
+ logical = False
+ start = 0
+ system = None
+
+class HDD(object):
+ bytes = 0
+ capacity = None
+ description = None
+ geometry = []
+ name = None
+ partitions = []
+ raw_device = None
-class Disk(object):
# fdisk -G /dev/rdsk/c0t0d0p0
# * Physical geometry for device /dev/rdsk/c0t0d0p0
# * PCYL NCYL ACYL BCYL NHEAD NSECT SECSIZ
# 2088 2088 0 0 255 63 512
_fdiskG = re.compile('\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*')
- # fdisk -W - /dev/rdsk/c0t0d0p0
- #* Id Act Bhead Bsect Bcyl Ehead Esect Ecyl Rsect Numsect
- # 191 128 65 2 0 168 44 380 4096 6111232
- # 0 0 0 0 0 0 0 0 0 0
- # 0 0 0 0 0 0 0 0 0 0
- # 0 0 0 0 0 0 0 0 0 0
- _fdiskW = re.compile('\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*')
-
- _name = None
- _desc = None
- _geometry = {}
- _capacity = 0
- _capacity_human = None
- _partitions = []
- _raw_device = None
-
- @property
- def name(self):
- return self._name
-
- @property
- def desc(self):
- return self._desc
-
- @desc.setter
- def desc(self, desc):
- self._desc = desc
-
- def read_partitions():
- self._partitions = {}
- out = subprocess.Popen(['fdisk', '-W', '-', self._raw_device],
- stdout=subprocess.PIPE).stdout
- for line in out:
- m = self._fdiskW.match(line)
+ # fdisk.ul -l /dev/rdsk/c0t4d0p0
+ # XXX note a bug: p0p1, must be p1.
+ # Device Boot Start End Blocks Id System
+ # /dev/rdsk/c0t4d0p0p1 63 15631244 7815591 bf Solaris
+ # /dev/rdsk/c0t4d0p0p2 15631245 33543719 8956237+ 5 Extended
+ # /dev/rdsk/c0t4d0p0p5 15631308 23438834 3903763+ 82 Linux swap / Solaris
+ # /dev/rdsk/c0t4d0p0p6 33479523 33543719 32098+ 83 Linux
+
+
+ def reread_partitions(self):
+ # boot? start end blocks id system
+ fdisk_ul = re.compile('^/dev/rdsk/{}\S+\s+(\*|\s)\s+(\d+)\s+(\d+)\s+(\d+)\+?\s+([a-f0-9]+)\s(.+)\s*$'.format(self.name))
+ fdisk_ul_cmd = Popen(['fdisk.ul', '-l', self.raw_device], stdout=PIPE, stderr=PIPE)
+ logical = False
+ self.partitions = []
+ for line in fdisk_ul_cmd.stdout:
+ m = fdisk_ul.match(line)
if m:
- self._partitions.append({
- 'id' : int(m.group(1)),
- 'rsect' : int(m.group(9)),
- 'numsect' : int(m.group(10)),
- 'cap' : humanCapasity(int(m.group(10))*self._geometry['secsiz']),
- })
-
- @name.setter
- def name(self, name):
- """ Set disk name and update disk's info: capacity, geometry etc."""
- if name != self._name:
- self._raw_device = '/dev/rdsk/{}p0'.format(name)
- self._name = name
- out = subprocess.Popen(['fdisk', '-G', self._raw_device],
- stdout=subprocess.PIPE).stdout
- for line in out:
- m = self._fdiskG.match(line)
- if m:
- self._geometry = {
- 'pcyl' : int(m.group(1)),
- 'ncyl' : int(m.group(2)),
- 'acyl' : int(m.group(3)),
- 'bcyl' : int(m.group(4)),
- 'nhead' : int(m.group(5)),
- 'nsect' : int(m.group(6)),
- 'secsiz' : int(m.group(7)),
- }
- self._capacity = ( self._geometry['ncyl'] * self._geometry['nhead'] *
- self._geometry['nsect'] * self._geometry['secsiz'] )
- assert self._capacity != 0
- self._capacity_human = humanCapasity(self._capacity)
-
- @property
- def geometry(self):
- return self._geometry
-
- @property
- def partitions(self):
- return self._partitions
-
- @property
- def capacity(self):
- """ Capacity in bytes """
- return self._capacity
-
- @property
- def cap(self):
- """ Human readable capacity string, e. g. 30 GB """
- return self._capacity_human
-
- def __init__(self, name, desc=None):
+ part_id = int(m.group(5), 16)
+ # Ignore "Extended" partitions, mark subsequent partitions as "logical"
+ if not logical and part_id == 0x5:
+ logical = True
+ continue
+
+ p = Partition()
+ p.boot = m.group(1) == '*'
+ p.start = int(m.group(2))
+ p.end = int(m.group(3))
+ p.blocks = int(m.group(4))
+ p.id = part_id
+ p.system = m.group(6)
+ p.logical = logical
+ p.bytes = (p.end - p.start) * self.geometry['secsiz']
+ p.capacity = human_capacity(p.bytes)
+ self.partitions.append(p)
+
+ def __init_capacity(self):
+ fdisk_cmd = Popen(['fdisk', '-G', self.raw_device], stdout=PIPE)
+ for line in fdisk_cmd.stdout:
+ m = self._fdiskG.match(line)
+ if m:
+ self.geometry = {
+ 'pcyl' : int(m.group(1)),
+ 'ncyl' : int(m.group(2)),
+ 'acyl' : int(m.group(3)),
+ 'bcyl' : int(m.group(4)),
+ 'nhead' : int(m.group(5)),
+ 'nsect' : int(m.group(6)),
+ 'secsiz' : int(m.group(7)),
+ }
+
+
+ self.bytes = ( self.geometry['ncyl'] * self.geometry['nhead'] *
+ self.geometry['nsect'] * self.geometry['secsiz'] )
+ self.capacity = human_capacity(self.bytes)
+
+
+
+ def __init__(self, name, description):
self.name = name
- self.desc = desc
- pass
+ self.raw_device = '/dev/rdsk/{}p0'.format(name)
+ self.description = description
+ self.__init_capacity()
+ self.reread_partitions()
diff --git a/setup b/setup
deleted file mode 100755
index 23d091e..0000000
--- a/setup
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/python
-
-import dialog
-import subprocess
-import sys
-import re
-import pprint
-
-from lib.hdd import Disk
-
-d = dialog.Dialog(dialog='whiptail')
-d.setBackgroundTitle('Dyson Installer')
-
-class Installer(object):
- HDD = {}
- RPOOL = 'rpool'
- RHDD = None
- # method: (if true, if false)
- flow = {}
-
- def _read_hdd(self):
- pat = re.compile('\d+\.\s+(\S+)\s+<(\S+)\s*.+>')
- out = subprocess.Popen('format </dev/null', shell=True, stdout=subprocess.PIPE).stdout
- self.HDD = {}
- for line in out:
- m = pat.search(line)
- if m:
- name = m.group(1)
- self.HDD[name] = Disk(name, m.group(2))
- if len(self.HDD) == 0:
- d.msgbox(width=50, title='Error: no disks found',
- text='\nSorry, no hard disks found on your system. Installation is not possible.')
- return False
- else:
- return True
-
-
- def choose_hdd(self):
- choices = []
- for hdd in sorted(self.HDD):
- choices.append((hdd, self.HDD[hdd].cap + ' ' + self.HDD[hdd].desc))
-
- cancel, self.RHDD = d.menu(choices=choices, width=70, height=20,
- title='Choose a disk for ZFS root pool',
- text='')
- return not cancel
-
- def welcome(self):
- return not d.yesno(width=60, height=20, title='Welcome to Dyson installer',
- text='''
-Dyson is an operating system derived from Debian and based on Illumos core. \
-It uses Illumos unix kernel and libc, ZFS file system and SMF to manage system startup. \
-To learn more visit http://osdyson.org
-
-This installer will guide you though disk paritioning, filesystem creation, \
-installation of base system and minimal configuration.
-
-Please note, this system IS VERY EXPERIMENTAL. \
-You can LOOSE ALL YOUR DATA which this system can reach ;-)
-
-Whould you like to continue?''')
-
- def choose_rpool_name(self):
- cancel, pool = d.inputbox(width=40, title='ZFS root pool',
- text='\nPlease, enter a name for ZFS root pool. Default is "rpool":',
- init=self.RPOOL)
- if cancel:
- return False
- else:
- self.RPOOL = pool
- return True
-
- def __init__(self):
- self._read_hdd()
- self.flow['welcome'] = ('choose_hdd', 'cancel')
- pass
-
- def run(self, method='welcome'):
- rc = getattr(self, method)()
- if method in self.flow:
- t, f = self.flow[method]
- if rc:
- self.run(t)
- else:
- self.run(f)
- else:
- self.done()
-
- def done(self, code=0):
- sys.exit(0)
-
- def cancel(self):
- self.done(1)
-
-
-
-i = Installer()
-i.run()