diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-03-31 23:34:57 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-04-01 00:32:30 +0000 |
commit | ad96e366d10a8b5fc49794a646eac6c5b5b0a949 (patch) | |
tree | 23b3fe554035c86e8be7eaedcdf6bf1c20036289 | |
parent | 6e6dea80e5c938494c71796c9d7bd977f5c21db9 (diff) | |
download | live-ad96e366d10a8b5fc49794a646eac6c5b5b0a949.tar.gz |
New installer: use snack (newt); can configure disks
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | install | 175 | ||||
-rw-r--r-- | lib/hdd/__init__.py | 182 | ||||
-rwxr-xr-x | setup | 98 |
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 @@ -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() @@ -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() |