You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-install/pym/install/install.py

559 lines
24 KiB

9 years ago
# -*- coding: utf-8 -*-
14 years ago
# Copyright 2010-2016 Mir Calculate. http://www.calculate-linux.org
14 years ago
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import re
import sys
import time
from os import path
from random import choice
import string
import glob
import shutil
from time import sleep
9 years ago
from calculate.core.server.func import MethodsInterface
from calculate.lib.utils.files import (pathJoin,
isMount, process, listDirectory,
checkUtils, readFile, find, copyWithPath,
readLinesFile, getProgPath)
from calculate.lib.utils.device import (detectDeviceForPartition,
9 years ago
getUdevDeviceInfo, refreshLVM,
refreshUdev, countPartitions)
from datavars import DataVarsInstall
from calculate.install.variables.autopartition import AutoPartition
from distr import DistributiveError
from subprocess import Popen, PIPE, STDOUT
from itertools import *
14 years ago
9 years ago
class InstallError(Exception):
"""Installation Error"""
9 years ago
from migrate_users import migrate
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate, _
setLocalTranslate('cl_install3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)
14 years ago
9 years ago
class Install(MethodsInterface):
"""Primary class for templates applying and system installation"""
def __init__(self):
self.clVars = None
# refresh information about LVM
refreshLVM()
# refresh information about device in udevadm info
refreshUdev()
9 years ago
def initVars(self, datavars=None):
"""Primary initialization of variables"""
if not datavars:
self.clVars = DataVarsInstall()
self.clVars.importInstall()
self.clVars.flIniFile()
else:
self.clVars = datavars
9 years ago
def canInstallGrub2(self, target):
13 years ago
"""Check that system has grub2 in current and installed system"""
if self.clVars.Get('os_grub2_path'):
return bool(
9 years ago
filter(lambda x: (x.startswith('grub-1.99') or
x.startswith('grub-2')),
listDirectory('/var/db/pkg/sys-boot')))
13 years ago
return False
9 years ago
def prepareBoot(self, target_distr):
"""Prepare system for boot"""
if self.clVars.Get('os_install_root_type') == "flash":
9 years ago
self.installSyslinuxBootloader(target_distr)
else:
9 years ago
if self.canInstallGrub2(target_distr):
self.installGrub2Bootloader(target_distr)
13 years ago
else:
9 years ago
self.installLegacyGrubBootloader(target_distr)
return True
9 years ago
def setActivePartition(self, partition):
"""
Установка активного раздела для dos и gpt таблицы разделов
"""
9 years ago
device_name = detectDeviceForPartition(partition)
if device_name is None:
raise DistributiveError(
9 years ago
_("Failed to determine the parent device for %s") % partition)
# device hasn't any partition
9 years ago
elif device_name == "":
return True
9 years ago
fdisk_cmd, gdisk_cmd, parted_cmd = checkUtils('/sbin/fdisk',
'/usr/sbin/gdisk',
'/usr/sbin/parted')
12 years ago
disk = self.clVars.Select('os_install_disk_parent',
9 years ago
where='os_install_disk_dev', eq=partition,
12 years ago
limit=1)
9 years ago
partition_table = self.clVars.Select('os_device_table',
where='os_device_dev',
eq=disk, limit=1)
partition_number = (
getUdevDeviceInfo(name=partition).get('ID_PART_ENTRY_NUMBER', '') or
getUdevDeviceInfo(name=partition).get('UDISKS_PARTITION_NUMBER', '')
)
device_partition_count = countPartitions(device_name)
if device_name and not partition_number:
raise DistributiveError(
9 years ago
_("Failed to determine the partition number for %s")
% partition)
boot_flag = "boot" if partition_table == "dos" else "legacy_boot"
if partition_table == "dos":
fdisk = process(fdisk_cmd, "-l", device_name)
DEVICENUM, AFLAG = 0, 1
change_active = map(
lambda x: x[DEVICENUM],
filter(
lambda x: (x[DEVICENUM] != partition_number and
x[AFLAG] == "*" or
x[DEVICENUM] == partition_number and
not x[AFLAG] == "*"),
list(map(
lambda x: [str(x[0]), x[1][1].strip()],
# enumerate partitions
enumerate(filter(None, map(
lambda x: x.split()[:2],
# drop string before information about partitions
dropwhile(
lambda x: not x.lstrip().startswith(
"Device"),
fdisk.readlines()))))))[1:]))
else:
9 years ago
parted = process(parted_cmd, "-m", device_name, "print")
DEVICENUM, FLAGS = 0, 6
change_active = map(
lambda x: x[DEVICENUM], filter(
lambda x: (x[DEVICENUM] != partition_number and
boot_flag in x[FLAGS].strip(';').split(', ') or
x[DEVICENUM] == partition_number and
boot_flag not in
x[FLAGS].strip(';').split(', ')),
filter(lambda x: len(x) >= 7,
map(lambda x: x.split(':'),
parted.readlines()[2:]))))
if not change_active:
return True
9 years ago
if partition_table == "dos":
pipe = Popen([fdisk_cmd, device_name],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
for part_num in change_active:
pipe.stdin.write("a\n%s\n" % part_num)
pipe.stdin.write("w\n")
pipe.stdin.close()
pipe.wait()
9 years ago
elif partition_table == "gpt":
pipe = Popen([gdisk_cmd, device_name],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
if device_partition_count > 1:
pipe.stdin.write("x\n")
9 years ago
for part_num in change_active:
pipe.stdin.write("a\n%s\n2\n\n" % part_num)
pipe.stdin.write("w\nY\n")
else:
pipe.stdin.write("x\na\n2\n\nw\nY\n")
pipe.stdin.close()
pipe.wait()
9 years ago
for wait_time in (0.1, 0.2, 0.5, 1, 2, 4):
if path.exists(partition):
return True
else:
9 years ago
sleep(wait_time)
raise InstallError(
9 years ago
_("Failed to find partition %s after changing the activity") %
partition)
9 years ago
def installSyslinuxBootloader(self, target):
"""
Установить syslinux загрузчик (используется для flash)
"""
if not self.clVars.Get('os_install_mbr'):
return
# прописать MBR
9 years ago
dd_process = process("/bin/dd", "if=/usr/share/syslinux/mbr.bin",
"of=%s" % self.clVars.Get('os_install_mbr')[0],
stderr=STDOUT)
if dd_process.failed():
raise DistributiveError(
9 years ago
_("Failed to write the master boot record\n%s") %
dd_process.read())
target.close()
# выполнить установку syslinux загрузчика
9 years ago
install_root_dev = self.clVars.Get('os_install_root_dev')
syslinux_process = process("/usr/bin/syslinux",
install_root_dev, stderr=STDOUT)
if syslinux_process.failed():
raise DistributiveError(_("Failed to install syslinux\n%s") %
syslinux_process.read())
# установить загрузочный раздел активным
return self.setActivePartition(self.clVars.Get('os_install_root_dev'))
9 years ago
def installGrub2Bootloader(self, target):
"""
Установка GRUB2 загрузчика
"""
# проверить наличие grub2
9 years ago
cmd_grub_install = self.clVars.Get('os_grub2_path')
if not cmd_grub_install:
raise DistributiveError(_("Failed to install the bootloader"))
process("sync").success()
# если установка GRUB2 производится на текущую систему
# загруженную в builder режиме
9 years ago
if (self.clVars.Get('os_install_scratch') == "on" and
self.clVars.Get('cl_action') != "system"):
prefix_boot = "/mnt/scratch"
else:
9 years ago
prefix_boot = "/"
# установка UEFI
if self.clVars.GetBool('os_install_uefi_set'):
9 years ago
self.install_grub_uefi(cmd_grub_install, prefix_boot, target)
# не UEFI установка
else:
9 years ago
self.install_grub_biosboot(cmd_grub_install, prefix_boot, target)
def install_grub_biosboot(self, cmd_grub_install, prefix_boot, target):
"""
Установить GRUB, загрузчик MBR (GPT)
"""
# получить загрузочный раздел (если есть /boot, то
# он является загрузочным иначе корень)
for boot_path in ("/boot", "/"):
boot_disk = self.clVars.Select("os_install_disk_dev",
where="os_install_disk_mount",
eq=boot_path, limit=1)
if boot_disk:
self.setActivePartition(boot_disk)
break
# если GRUB2 версии 2.00 и выше, обычная установка требует
# параметра --target=i386-pc, иначе GRUB2 может попытаться
# прописать себя как UEFI
if filter(lambda x: "2." in x,
process(cmd_grub_install, '--version')):
platform = ["--target=i386-pc"]
else:
platform = []
# прописать GRUB2 на все указанные диски
for mbr_disk in self.clVars.Get('os_install_mbr'):
grub_process = process(cmd_grub_install,
"--boot-directory=%s" % pathJoin(
prefix_boot,
target.getBootDirectory()),
mbr_disk, "--force", *platform,
stderr=STDOUT, envdict=os.environ)
if grub_process.failed():
raise DistributiveError(
_("Failed to install the bootloader"))
def install_grub_uefi(self, cmd_grub_install, prefix_boot, target):
"""
Установить grub с UEFI загрузчиком
"""
grub_params = [
"--boot-directory=%s" % pathJoin(
prefix_boot,
target.getBootDirectory()),
"--target=x86_64-efi",
"--efi-directory=%s" %
target.getEfiDirectory(),
"--force"]
# в случае установки на usb-hdd EFI загрузчик не прописывается
# в efivars
if self.clVars.Get('os_install_root_type') == 'usb-hdd':
grub_params.append("--removable")
if self.clVars.Get('cl_action') != "system" and \
not isMount('/boot/efi'):
raise DistributiveError(_("Failed to install the bootloader. "
"/boot/efi is not mounted."))
grub_process = process(cmd_grub_install,
*grub_params, stderr=STDOUT,
envdict=os.environ)
if grub_process.failed():
raise DistributiveError(_("Failed to install the bootloader"))
# проверяем успешность создания загрузочной записи
# если среди загрузочных записей отсутствует запись
# calculate и dmesg содержит сообщение об ошибке efivars -
# запись создать не удалось
efi_boot_mgr = getProgPath('/usr/sbin/efibootmgr')
dmesg = getProgPath('/bin/dmesg')
if efi_boot_mgr and dmesg:
if not re.search('Boot.*calculate',
process(efi_boot_mgr).read(), re.M) and \
re.search('efivars.*set_variable.*failed',
process(dmesg).read(), re.M):
raise DistributiveError(
_("Failed to create the UEFI boot record"))
def installLegacyGrubBootloader(self, target):
"""
Install legecy grub boot loader
14 years ago
Perform grub installation to disk, which has root partition
"""
9 years ago
cmd_grub = getProgPath('/sbin/grub')
if not cmd_grub:
raise DistributiveError(_("Failed to install the bootloader"))
9 years ago
grub_process = process(
cmd_grub,
"--device-map=%s/boot/grub/device.map" % target.getDirectory(),
"--batch", stderr=STDOUT)
boot_disk = self.clVars.Select('os_install_disk_grub',
where='os_install_disk_mount',
_in=('/', '/boot'),
sort='DESC', limit=1)
if not boot_disk:
raise DistributiveError(_("Failed to determine the boot disk"))
9 years ago
self.setActivePartition(boot_disk)
for mbr_disk in self.clVars.Get('os_install_mbr'):
mbr_disk_num = self.clVars.Select("os_device_map",
where="os_device_dev",
eq=mbr_disk)
if not mbr_disk_num and mbr_disk_num != 0:
raise DistributiveError(_("Failed to determine mbr"))
9 years ago
for line in ("root (hd%s)" % boot_disk,
"setup (hd%d)" % mbr_disk_num,
"quit"):
9 years ago
grub_process.write("%s\n" % line)
if grub_process.failed():
raise DistributiveError(_("Failed to install the bootloader"))
9 years ago
def setupOpenGL(self):
"""
Выполнить выбор opengl для текущего видеодрайвера
"""
9 years ago
default_gl = "xorg-x11"
path_gl_modules = path.join(self.clVars.Get('cl_chroot_path'),
'usr/lib/opengl')
open_gl_env = path.join(self.clVars.Get('cl_chroot_path'),
'etc/env.d/03opengl')
open_gl_mods = filter(lambda x: x != "global",
listDirectory(path_gl_modules))
map_gl_drivers = {'fglrx': ("ati" if "ati" in open_gl_mods
else default_gl),
'nvidia': "nvidia" if "nvidia" in open_gl_mods
else default_gl}
x11_driver = self.clVars.Get('os_install_x11_video_drv')
9 years ago
if x11_driver in map_gl_drivers:
new_module_name = map_gl_drivers[x11_driver]
else:
new_module_name = default_gl
current_module_name = map(
lambda x: x.strip().rpartition('=')[-1].strip('"\''),
filter(lambda x: x.startswith("OPENGL_PROFILE="),
readLinesFile(open_gl_env)))
if current_module_name:
current_module_name = current_module_name[-1]
else:
9 years ago
current_module_name = ""
if current_module_name == new_module_name:
return True
9 years ago
return process('/usr/bin/eselect', 'opengl', 'set',
new_module_name).success()
def checkVideoDriver(self):
"""
Проверить видео драйвер, и если это nvidia, то
обновить маску на пакет видеодрайвера
"""
if self.clVars.Get('hr_video') != 'nvidia':
return True
9 years ago
mask_file = '/etc/portage/package.mask'
nvidia_mask_file = path.join(mask_file, 'nvidia')
# если package.mask является файлом - делаем его директорией
9 years ago
if path.isfile(mask_file):
os.rename(mask_file, mask_file + "2")
os.mkdir(mask_file, mode=0755)
os.rename(mask_file + "2", path.join(mask_file, "default"))
current_nvidia_mask = readFile(nvidia_mask_file).strip()
new_nvidia_mask = self.clVars.Get('os_nvidia_mask')
if new_nvidia_mask == current_nvidia_mask:
return True
9 years ago
open(nvidia_mask_file, 'w').write(new_nvidia_mask)
return True
9 years ago
def changeScheduler(self, scheduler):
"""
Изменить текущий IO планировщик
"""
root_dev = self.clVars.Select('os_disk_parent',
9 years ago
where='os_disk_mount',
eq='/', limit=1)
try:
9 years ago
scheduler_path = (
"/sys%s/queue/scheduler" %
(getUdevDeviceInfo(name=root_dev).get('DEVPATH', '')))
if path.exists(scheduler_path):
open(scheduler_path, 'w').write(scheduler)
except Exception:
raise InstallError(_("Unable to change the I/O scheduler"))
return True
9 years ago
def autopartition(self, table, devices, data, lvm, lvm_vgname, bios_grub,
bios_grub_size):
"""
Авторазметка диска с таблицей разделов 'table', диски указываются
'device', параметры таблицы 'data', 'lvm' использование LVM,
'lvm_vgname' название группы томов LVM, bios_grub - создавать
bios_grub раздел, bios_grub_size - раздел bios grub раздела в байтах
"""
ap = AutoPartition()
9 years ago
ap.clearLvm(devices, self.clVars)
ap.clearRaid(devices, self.clVars)
ap.recreateSpace(table, devices, data, lvm,
lvm_vgname, bios_grub, bios_grub_size)
return True
9 years ago
def format(self, target):
"""
Форматировать разделы для 'target' дистрибутива
"""
target.performFormat()
return True
9 years ago
def unpack(self, source, target, files_num):
"""
Распаковать 'source' в 'target', 'filesnum' количество копируемых файлов
"""
self.addProgress()
9 years ago
if files_num.isdigit():
files_num = int(files_num)
else:
9 years ago
files_num = 0
target.installFrom(source, callbackProgress=self.setProgress,
9 years ago
filesnum=files_num)
return True
9 years ago
def copyClt(self, source, target, cltpath):
"""
Скопировать clt шаблоны из 'cltpath' в 'target' дистрибутив из
'source' дистрибутива
"""
9 years ago
target_dir = target.getDirectory()
source_dir = source.getDirectory()
for f in filter(lambda x: x.endswith('.clt'),
chain(*map(lambda x: find(pathJoin(source_dir, x),
filetype="f"),
cltpath))):
copyWithPath(f, target_dir, prefix=source_dir)
return True
9 years ago
def copyOther(self, source, target):
"""
Скопировать прочие настройки из текущей системы в новую
"""
9 years ago
file_mask = re.compile("(/etc/ssh/ssh_host_.*|"
"/root/.ssh/(id_.*|known_hosts))")
9 years ago
target_dir = target.getDirectory()
source_dir = source.getDirectory()
for f in filter(file_mask.search,
chain(*map(lambda x: find(pathJoin(source_dir, x),
filetype="f"),
["/etc", "/root/.ssh"]))):
copyWithPath(f, target_dir, prefix=source_dir)
return True
12 years ago
def rndString(self):
"""
Получить произвольную строку из 8 символов
"""
"""Get random string with len 8 char"""
9 years ago
return "".join([choice(string.ascii_letters + string.digits)
for i in xrange(0, 8)])
9 years ago
def _getFreeDirectory(self, directory):
12 years ago
"""
Получить название директории
12 years ago
"""
9 years ago
new_dir_name = directory
while path.exists(new_dir_name):
new_dir_name = "%s.%s" % (directory, self.rndString())
return new_dir_name
12 years ago
def remountNTFS(self):
"""
Перемонтировать NTFS разделы для работы os-prober
12 years ago
"""
res = True
12 years ago
for disk in self.clVars.Select('os_disk_dev',
9 years ago
where='os_disk_format', like='ntfs'):
mount_dir = self._getFreeDirectory('/var/lib/calculate/mount.ntfs')
12 years ago
try:
9 years ago
os.mkdir(mount_dir)
except (OSError, IOError):
12 years ago
continue
9 years ago
if process('/bin/mount', disk, mount_dir).success():
for i in (0.2, 0.5, 1, 2, 4, 5):
if process('/bin/umount', mount_dir).success():
12 years ago
break
time.sleep(i)
12 years ago
else:
9 years ago
self.printWARNING(_("Unable to umount %s") % mount_dir)
res = False
12 years ago
try:
9 years ago
os.rmdir(mount_dir)
except (OSError, IOError):
self.printWARNING(
_("Unable to remove directory %s") % mount_dir)
return False
return res
9 years ago
def mountBind(self, target):
"""
Подключить bind точки монтирования у дистрибутива
"""
target.postinstallMountBind()
return True
9 years ago
def userMigrate(self, target, migrate_data, root_pwd):
"""
Перенос текущих пользователей в новую систему,
установка пароля пользователя root
"""
9 years ago
migrator = migrate(target.getDirectory())
if not migrator.migrate(migrate_data, root_pwd, [], [], ):
raise InstallError(_("Failed to migrate users onto the new system"))
return True
9 years ago
def umount(self, distr):
"""
Отключить дистрибутив
"""
distr.close()
return True
def drop_xorg_logs(self):
"""
Сбросить логи загрузки xorg сервера
"""
for fn in glob.glob("/var/log/Xorg.*.log"):
new_name = "%s.old" % fn
if path.exists(new_name):
os.unlink(new_name)
shutil.move(fn, new_name)
return True