/[gli]/branches/overhaul/src/GLIArchitectureTemplate.py
Gentoo

Diff of /branches/overhaul/src/GLIArchitectureTemplate.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1590 Revision 1591
61 # where each entry is a function (with no arguments) that carries out the desired actions. 61 # where each entry is a function (with no arguments) that carries out the desired actions.
62 # Of course, steps will be different depending on the install_profile 62 # Of course, steps will be different depending on the install_profile
63 63
64 self._architecture_name = "generic" 64 self._architecture_name = "generic"
65 self._install_steps = [ 65 self._install_steps = [
66 # { 'function': self.partition, 'name': "Partition"},
67 { 'function': self.mount_local_partitions, 'name': "Mount local partitions"}, 66 { 'function': self.mount_local_partitions, 'name': "Mount local partitions"},
68 { 'function': self.mount_network_shares, 'name': "Mount network (NFS) shares"}, 67 { 'function': self.mount_network_shares, 'name': "Mount network (NFS) shares"},
69 { 'function': self.unpack_stage_tarball, 'name': "Unpack stage tarball"}, 68 { 'function': self.unpack_stage_tarball, 'name': "Unpack stage tarball"},
70 { 'function': self.update_config_files, 'name': "Updating config files"}, 69 { 'function': self.update_config_files, 'name': "Updating config files"},
71 { 'function': self.configure_make_conf, 'name': "Configure /etc/make.conf"}, 70 { 'function': self.configure_make_conf, 'name': "Configure /etc/make.conf"},
1341 GLIUtility.spawn("mv " + self._compile_logfile + " " + self._compile_logfile + ".failed") 1340 GLIUtility.spawn("mv " + self._compile_logfile + " " + self._compile_logfile + ".failed")
1342# GLIUtility.spawn("rm /tmp/compile_output.log") 1341# GLIUtility.spawn("rm /tmp/compile_output.log")
1343 GLIUtility.spawn("mv " + LOGFILE + " " + LOGFILE + ".failed") 1342 GLIUtility.spawn("mv " + LOGFILE + " " + LOGFILE + ".failed")
1344# GLIUtility.spawn("rm /var/log/installer.log") 1343# GLIUtility.spawn("rm /var/log/installer.log")
1345 1344
1346######################################################
1347######### Partitioning Code ##########################
1348
1349
1350 def _sectors_to_megabytes(self, sectors, sector_bytes=512):
1351 return float((float(sectors) * sector_bytes)/ float(MEGABYTE))
1352
1353 def _wait_for_device_node(self, devnode):
1354 if GLIUtility.is_file("/sbin/udevsettle"):
1355 GLIUtility.spawn("/sbin/udevsettle")
1356 if not GLIUtility.is_file(devnode):
1357 GLIUtility.spawn("/sbin/udevsettle")
1358 else:
1359 for i in range(0, 10):
1360 if GLIUtility.is_file(devnode):
1361 break
1362 time.sleep(1)
1363 time.sleep(1)
1364 for i in range(0, 10):
1365 if GLIUtility.is_file(devnode):
1366 break
1367 time.sleep(1)
1368
1369 def _add_partition(self, disk, start, end, type, fs, name="", strict_start=False, strict_end=False):
1370 if self._debug: self._logger.log("_add_partition(): type=%s, fstype=%s" % (type, fs))
1371 types = { 'primary': parted.PARTITION_PRIMARY, 'extended': parted.PARTITION_EXTENDED, 'logical': parted.PARTITION_LOGICAL }
1372 fsTypes = {}
1373 fs_type = parted.file_system_type_get_next ()
1374 while fs_type:
1375 fsTypes[fs_type.name] = fs_type
1376 fs_type = parted.file_system_type_get_next (fs_type)
1377 fstype = None
1378 if fs == "apple_bootstrap":
1379 fs = "hfs"
1380 if fs: fstype = fsTypes[fs]
1381 newpart = disk.partition_new(types[type], fstype, start, end)
1382 constraint = disk.dev.constraint_any()
1383 if strict_start:
1384 constraint.start_range.set_start(start)
1385 constraint.start_range.set_end(start)
1386 constraint.end_range.set_start(end)
1387 if strict_end:
1388 constraint.start_range.set_start(start)
1389 constraint.end_range.set_start(end)
1390 constraint.end_range.set_end(end)
1391 disk.add_partition(newpart, constraint)
1392 if self._debug: self._logger.log("_add_partition(): partition added")
1393
1394 def _delete_partition(self, parted_disk, minor):
1395 try:
1396 parted_disk.delete_partition(parted_disk.get_partition(minor))
1397 except:
1398 self._logger.log("_delete_partition(): could not delete partition...ignoring (for now)")
1399
1400 def _check_table_changed(self, oldparts, newparts):
1401 for part in newparts:
1402 oldpart = oldparts[part]
1403 newpart = newparts[part]
1404 if not newparts[part]['origminor'] or not oldparts.get_partition(part):
1405 return True
1406 if oldpart['type'] == newpart['type'] and long(oldpart['mb']) == long(newpart['mb']) and not newpart['resized'] and not newpart['format']:
1407 continue
1408 else:
1409 return True
1410 return False
1411
1412 def _check_table_layout_changed(self, oldparts, newparts):
1413 # This function is similar to the above function except it will see it as un-changed even if a partition is just being reformatted
1414 for part in newparts:
1415 oldpart = oldparts[part]
1416 newpart = newparts[part]
1417 if not newparts[part]['origminor'] or not oldparts.get_partition(part):
1418 return True
1419 if oldpart['type'] == newpart['type'] and long(oldpart['mb']) == long(newpart['mb']) and not newpart['resized']:
1420 continue
1421 else:
1422 return True
1423 return False
1424
1425 def _find_existing_in_new(self, oldminor, newparts):
1426 for part in newparts:
1427 if newparts[part]['origminor'] == oldminor:
1428 return part
1429 return 0
1430
1431 def _check_keeping_any_existing(self, newparts):
1432 for part in newparts:
1433 if newparts[part]['origminor']: return True
1434 return False
1435
1436 def _find_next_partition(self, curminor, parts):
1437 foundmyself = False
1438 for part in parts:
1439 if not part == curminor and not foundmyself: continue
1440 if part == curminor:
1441 foundmyself = True
1442 continue
1443 if foundmyself:
1444 return part
1445 return 0
1446
1447 def _find_current_minor_for_part(self, device, start):
1448 tmp_oldparts = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
1449 tmp_oldparts.set_partitions_from_disk()
1450 for tmp_oldpart in tmp_oldparts:
1451 self._logger.log("_find_current_minor_for_part(): looking at minor %s...start sector is %s...looking for %s" % (str(tmp_oldpart), str(tmp_oldparts[tmp_oldpart]['start']), str(start)))
1452 if tmp_oldparts[tmp_oldpart]['start'] == start:
1453 return tmp_oldparts[tmp_oldpart]['minor']
1454 else:
1455 raise GLIException("PartitionResizeError", 'fatal', '_find_current_minor_for_part', "Could not determine the new devnode for partition starting at sector " + str(start))
1456
1457 def _partition_delete_step(self, parted_disk, oldparts, newparts):
1458 self._logger.log("_partition_delete_step(): Deleting partitions that aren't being resized")
1459 for oldpart in list(oldparts)[::-1]:
1460 tmppart_old = oldparts[oldpart]
1461 if oldparts.get_disklabel() != "mac" and tmppart_old['type'] == "free": continue
1462 if tmppart_old['type'] == "extended":
1463 # Iterate through logicals to see if any are being resized
1464 self._logger.log("_partition_delete_step(): logicals for extended part %d: %s" % (tmppart_old['minor'], str(tmppart_old.get_logicals())))
1465 for logpart in tmppart_old.get_logicals():
1466 newminor = self._find_existing_in_new(logpart, newparts)
1467 self._logger.log("_partition_delete_step(): newminor is " + str(newminor))
1468 if newminor and newparts[newminor]['resized']:
1469 self._logger.log(" Logical partition " + str(logpart) + " to be resized...can't delete extended")
1470 break
1471 else:
1472 self._logger.log(" No logical partitions are being resized...deleting extended")
1473 self._delete_partition(parted_disk, oldpart)
1474 else:
1475 newminor = self._find_existing_in_new(oldpart, newparts)
1476 if newminor and not newparts[newminor]['format']:
1477 if newparts[newminor]['resized']:
1478 self._logger.log(" Ignoring old minor " + str(oldpart) + " to resize later")
1479 continue
1480 else:
1481 self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated later")
1482 else:
1483 self._logger.log(" No match in new layout for old minor " + str(oldpart) + "...deleting")
1484 self._delete_partition(parted_disk, oldpart)
1485 parted_disk.commit()
1486
1487 def _partition_resize_step(self, parted_disk, device, oldparts, newparts):
1488 self._logger.log("_partition_resize_step(): Resizing partitions")
1489 device_sectors = newparts.get_num_sectors()
1490 for oldpart in oldparts:
1491 tmppart_old = oldparts[oldpart]
1492 newminor = self._find_existing_in_new(oldpart, newparts)
1493 if not newminor or not newparts[newminor]['resized'] or newparts[newminor]['type'] in ("extended", "free"):
1494 continue
1495 tmppart_new = newparts[newminor]
1496 type = tmppart_new['type']
1497 start = tmppart_new['start']
1498 end = start + (long(tmppart_new['mb']) * MEGABYTE / 512) - 1
1499
1500 # Make sure calculated end sector doesn't overlap start sector of next partition
1501 nextminor = self._find_next_partition(newminor, newparts)
1502 if nextminor:
1503 if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
1504 self._logger.log(" End sector for growing partition overlaps with start of next partition...fixing")
1505 end = newparts[nextminor]['start'] - 1
1506
1507 # cap to end of device
1508 if end >= device_sectors:
1509 end = device_sectors - 1
1510
1511 total_sectors = end - start + 1
1512 total_bytes = long(total_sectors) * 512
1513
1514 # Delete partition and recreate at same start point with new size if growing
1515 if tmppart_new['mb'] > tmppart_old['mb']:
1516 curminor = self._find_current_minor_for_part(device, start)
1517 self._delete_partition(parted_disk, curminor)
1518 if tmppart_new.is_logical():
1519 tmptype = "logical"
1520 else:
1521 tmptype = "primary"
1522 self._add_partition(parted_disk, start, end, tmptype, tmppart_new['type'], strict_start=True)
1523 parted_disk.commit()
1524
1525 curminor = self._find_current_minor_for_part(device, start)
1526 devnode = device + str(curminor)
1527
1528 self._wait_for_device_node(devnode)
1529
1530 if type in ("ext2", "ext3"):
1531 resizecmd = "resize2fs %s %sK" % (devnode, str(int((total_bytes - (2 * MEGABYTE)) / 1024)))
1532 self._logger.log("_partition_resize_step(): running: " + resizecmd)
1533 ret = GLIUtility.spawn(resizecmd, logfile=self._compile_logfile, append_log=True)
1534 if not GLIUtility.exitsuccess(ret):
1535 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize ext2/3 filesystem on " + devnode)
1536 elif type == "ntfs":
1537 ret = GLIUtility.spawn("yes | ntfsresize -v --size " + str(total_bytes) + " " + devnode, logfile=self._compile_logfile, append_log=True)
1538 if not GLIUtility.exitsuccess(ret):
1539 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize NTFS filesystem on " + devnode)
1540 elif type in ("linux-swap", "fat32", "fat16"):
1541 parted_fs = parted_disk.get_partition(curminor).geom.file_system_open()
1542 resize_constraint = parted_fs.get_resize_constraint()
1543 if total_sectors < resize_constraint.min_size or start != resize_constraint.start_range.start:
1544 raise GLIException("PartitionError", 'fatal', 'partition', "New size specified for " + devnode + " is not within allowed boundaries (blame parted)")
1545 new_geom = resize_constraint.start_range.duplicate()
1546 new_geom.set_start(start)
1547 new_geom.set_end(end)
1548 try:
1549 parted_fs.resize(new_geom)
1550 except:
1551 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize " + devnode)
1552 self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated in next pass")
1553# self._delete_partition(parted_disk, oldpart)
1554 parted_disk.delete_all()
1555 parted_disk.commit()
1556
1557 def _partition_recreate_step(self, parted_disk, newparts):
1558 self._logger.log("_partition_recreate_step(): Recreating partitions")
1559 start = 0
1560 end = 0
1561 extended_start = 0
1562 extended_end = 0
1563 device_sectors = newparts.get_num_sectors()
1564 self._logger.log(" Drive has " + str(device_sectors) + " sectors")
1565 for part in newparts:
1566 strict_start = False
1567 strict_end = False
1568 newpart = newparts[part]
1569 self._logger.log(" Partition " + str(part) + " has " + str(newpart['mb']) + "MB")
1570 if newpart['start']:
1571 self._logger.log(" Old start sector " + str(newpart['start']) + " retrieved")
1572 if start != newpart['start']:
1573 self._logger.log(" Retrieved start sector is not the same as the calculated next start sector (usually not an issue)")
1574 start = newpart['start']
1575 strict_start = True
1576 else:
1577 if newpart.is_logical() and start > extended_end:
1578 start = extended_start + 1
1579 self._logger.log(" Start sector calculated to be " + str(start))
1580 if extended_end and not newpart.is_logical() and start <= extended_end:
1581 self._logger.log(" Start sector for primary is less than the end sector for previous extended")
1582 start = extended_end + 1
1583 if newpart['end']:
1584 self._logger.log(" Old end sector " + str(newpart['end']) + " retrieved")
1585 end = newpart['end']
1586 part_sectors = end - start + 1
1587 strict_end = True
1588 else:
1589 part_sectors = long(newpart['mb']) * MEGABYTE / 512
1590 end = start + part_sectors
1591 if newpart.is_logical() and end > extended_end:
1592 end = extended_end
1593 self._logger.log(" End sector calculated to be " + str(end))
1594 # Make sure end doesn't overlap next partition's existing start sector
1595 nextminor = self._find_next_partition(newpart, newparts)
1596 if nextminor:
1597 if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
1598 self._logger.log(" End sector for partition overlaps with start of next partition...fixing")
1599 end = newparts[nextminor]['start'] - 1
1600 strict_end = True
1601 # cap to end of device
1602 if end >= device_sectors:
1603 end = device_sectors - 1
1604 # now the actual creation
1605 if newpart['type'] == "free":
1606 if newparts.get_disklabel() == "mac":
1607 # Create a dummy partition to be removed later because parted sucks
1608 self._logger.log(" Adding dummy partition to fool parted " + str(part) + " from " + str(start) + " to " + str(end))
1609 self._add_partition(parted_disk, start, end, "primary", "ext2", "free", strict_start=strict_start, strict_end=strict_end)
1610 elif newpart['type'] == "extended":
1611 self._logger.log(" Adding extended partition " + str(part) + " from " + str(start) + " to " + str(end))
1612 self._add_partition(parted_disk, start, end, "extended", "", strict_start=strict_start, strict_end=strict_end)
1613 extended_start = start
1614 extended_end = end
1615 elif not newpart.is_logical():
1616 self._logger.log(" Adding primary partition " + str(part) + " from " + str(start) + " to " + str(end))
1617 self._add_partition(parted_disk, start, end, "primary", newpart['type'], strict_start=strict_start, strict_end=strict_end)
1618 elif newpart.is_logical():
1619 if start >= extended_end:
1620 start = extended_start + 1
1621 end = start + part_sectors
1622 if nextminor and not newparts[nextminor].is_logical() and end > extended_end:
1623 end = extended_end
1624 self._logger.log(" Adding logical partition " + str(part) + " from " + str(start) + " to " + str(end))
1625 self._add_partition(parted_disk, start, end, "logical", newpart['type'], strict_start=strict_start, strict_end=strict_end)
1626 if self._debug: self._logger.log("partition(): flags: " + str(newpart['flags']))
1627 for flag in newpart['flags']:
1628 if parted_disk.get_partition(part).is_flag_available(flag):
1629 parted_disk.get_partition(part).set_flag(flag, True)
1630 if newpart['name'] and parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
1631 parted_disk.set_name(newpart['name'])
1632 # write to disk
1633 if self._debug: self._logger.log("partition(): committing change to disk")
1634 parted_disk.commit()
1635 if self._debug: self._logger.log("partition(): committed change to disk")
1636 start = end + 1
1637
1638 def _partition_format_step(self, parted_disk, device, newparts):
1639 self._logger.log("_partition_format_step(): Formatting new partitions")
1640 for part in newparts:
1641 newpart = newparts[part]
1642 devnode = newpart['devnode']
1643 # This little hack is necessary because parted sucks goat nuts
1644 if newparts.get_disklabel() == "mac" and newpart['type'] == "free":
1645 self._delete_partition(parted_disk, newpart)
1646 continue
1647 if newpart['format'] and newpart['type'] not in ('extended', 'free'):
1648# devnode = device + str(int(part))
1649 if self._debug: self._logger.log("_partition_format_step(): devnode is %s in formatting code" % devnode)
1650 # if you need a special command and
1651 # some base options, place it here.
1652 format_cmds = { 'linux-swap': "mkswap", 'fat16': "mkfs.vfat -F 16", 'fat32': "mkfs.vfat -F 32",
1653 'ntfs': "mkntfs", 'xfs': "mkfs.xfs -f", 'jfs': "mkfs.jfs -f",
1654 'reiserfs': "mkfs.reiserfs -f", 'ext2': "mkfs.ext2", 'ext3': "mkfs.ext3",
1655 'hfs': "hformat", 'apple_bootstrap': "hformat"
1656 }
1657 if newpart['type'] in format_cmds:
1658 cmdname = format_cmds[newpart['type']]
1659 else: # this should catch everything else
1660 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Unknown partition type " + newpart['type'])
1661 # sleep a bit first
1662 time.sleep(1)
1663 for tries in range(10):
1664 cmd = "%s %s %s" % (cmdname, newpart['mkfsopts'], devnode)
1665 self._logger.log(" Formatting partition %s as %s with: %s" % (str(part),newpart['type'],cmd))
1666 ret = GLIUtility.spawn(cmd, logfile=self._compile_logfile, append_log=True)
1667 if not GLIUtility.exitsuccess(ret):
1668 self._logger.log("Try %d failed formatting partition %s...waiting 5 seconds" % (tries+1, devnode))
1669 time.sleep(5)
1670 else:
1671 break
1672 else:
1673 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Could not create %s filesystem on %s" % (newpart['type'], devnode))
1674
1675 def partition(self):
1676 """
1677 TODO:
1678 skip fixed partitions in all passes (in GLISD maybe?)
1679 """
1680 parts_old = {}
1681 parts_new = self._install_profile.get_partition_tables()
1682 for device in GLIStorageDevice.detect_devices():
1683 parts_old[device] = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
1684 parts_old[device].set_partitions_from_disk()
1685
1686 self.notify_frontend("progress", (0, "Examining partitioning data"))
1687 total_steps = float(len(parts_new) * 4) # 4 for the number of passes over each device
1688 cur_progress = 0
1689 for device in parts_new:
1690 # Skip this device in parts_new if device isn't detected on current system
1691 if not device in parts_old:
1692 self._logger.log("There is no physical device " + device + " detected to match the entry in the install profile...skipping")
1693 continue
1694
1695 # This just makes things simpler in the code
1696 newparts = parts_new[device]
1697 oldparts = parts_old[device]
1698
1699 # Check to see if the old and new partition table structures are the same...skip if they are
1700 if not self._check_table_changed(oldparts, newparts):
1701 self._logger.log("Partition table for " + device + " is unchanged...skipping")
1702 continue
1703
1704 self._logger.log("partition(): Processing " + device + "...")
1705
1706 # Commit ritual sepuku if there are any mounted filesystems on this device
1707 if GLIUtility.spawn("mount | grep '^" + device + "'", return_output=True)[1].strip():
1708 raise GLIException("PartitionsMountedError", 'fatal', 'partition', "Cannot partition " + device + " due to filesystems being mounted")
1709
1710 # We also can't handle "unknown" partitions
1711 for part in newparts:
1712 if newparts[part]['type'] == "unknown":
1713 raise GLIException("UnknownPartitionTypeError", 'fatal', 'partition', "Refusing to partition this drive due to the presence of an unknown type of partition")
1714
1715 # Create pyparted objects for this device
1716 parted_dev = parted.PedDevice.get(device)
1717 try:
1718 parted_disk = parted.PedDisk.new(parted_dev)
1719 except:
1720 if self._debug: self._logger.log("partition(): could not load existing disklabel...creating new one")
1721 parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get((newparts.get_disklabel() or GLIStorageDevice.archinfo[self._architecture_name])))
1722
1723 # Iterate through new partitions and check for 'origminor' and 'format' == False
1724 for part in newparts:
1725 tmppart_new = newparts[part]
1726 if not tmppart_new['origminor'] or tmppart_new['format']: continue
1727 if not tmppart_new['origminor'] in oldparts:
1728 raise GLIException("MissingPartitionsError", 'fatal', 'partition', "Cannot find the existing partition that a new one refers to. This is not a bug. This is in fact your (the user's) fault. You should not reuse the installprofile.xml from a previous install that started the partitioning step.")
1729 tmppart_old = oldparts[tmppart_new['origminor']]
1730 if parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
1731 tmppart_new['name'] = tmppart_old['name']
1732 tmppart_new['flags'] = tmppart_old['flags']
1733 if tmppart_new['resized']:
1734 # Partition is being resized in the new layout
1735 self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + " and it being resized...saving start sector " + str(tmppart_old['start']))
1736 tmppart_new['start'] = tmppart_old['start']
1737 tmppart_new['end'] = 0
1738 else:
1739 # Partition is untouched in the new layout
1740 self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + "...saving start sector " + str(tmppart_old['start']) + " and end sector " + str(tmppart_old['end']))
1741 tmppart_new['start'] = tmppart_old['start']
1742 tmppart_new['end'] = tmppart_old['end']
1743
1744 if self._check_table_layout_changed(parts_old[device], parts_new[device]):
1745 # First pass to delete old partitions that aren't resized
1746 self.notify_frontend("progress", (cur_progress / total_steps, "Deleting partitioning that aren't being resized for " + device))
1747 cur_progress += 1
1748 self._partition_delete_step(parted_disk, oldparts, newparts)
1749
1750 # Second pass to resize old partitions that need to be resized
1751 self.notify_frontend("progress", (cur_progress / total_steps, "Resizing remaining partitions for " + device))
1752 cur_progress += 1
1753 self._partition_resize_step(parted_disk, device, oldparts, newparts)
1754
1755 # Wiping disk and creating blank disklabel
1756 try:
1757 parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get(newparts.get_disklabel()))
1758 parted_disk.commit()
1759 except:
1760 raise GLIException("DiskLabelCreationError", 'fatal', 'partition', "Could not create a blank disklabel!")
1761
1762 # Third pass to create new partition table
1763 self.notify_frontend("progress", (cur_progress / total_steps, "Recreating partition table for " + device))
1764 cur_progress += 1
1765 self._partition_recreate_step(parted_disk, newparts)
1766 else:
1767 cur_progress += 3
1768
1769 # Fourth pass to format partitions
1770 self.notify_frontend("progress", (cur_progress / total_steps, "Formatting partitions for " + device))
1771 cur_progress += 1
1772 self._partition_format_step(parted_disk, device, newparts)
1773
1774 # All done for this device
1775 self.notify_frontend("progress", (cur_progress / total_steps, "Done with partitioning for " + device))
1776 cur_progress += 1

Legend:
Removed from v.1590  
changed lines
  Added in v.1591

  ViewVC Help
Powered by ViewVC 1.1.20