ACC SHELL
/**
* File:
* modules/BootStorage.ycp
*
* Module:
* Bootloader installation and configuration
*
* Summary:
* Module includes specific functions for handling storage data.
* The idea is handling all storage data necessary for bootloader
* in one module.
*
* Authors:
* Jozef Uhliarik <juhliarik@suse.cz>
*
*
*
*
*/
{
module "BootStorage";
textdomain "bootloader";
import "Storage";
import "StorageDevices";
import "Arch";
import "Mode";
/**
* Saved change time from target map - only for MapAllPartitions()
*/
global integer disk_change_time_InitBootloader = nil;
/**
* Saved change time from target map - only for MapAllPartitions()
*/
global integer disk_change_time_MapAllPartitions = nil;
/**
* Saved change time from target map - only for checkCallingDiskInfo()
*/
global integer disk_change_time_checkCallingDiskInfo = nil;
/**
* bnc #468922 - problem with longtime running the parsing a huge number of disks
* map<string,map> the map of all partitions with info about it ->
* necessary for Dev2MountByDev() in routines/misc.ycp
*/
global map<string,map> all_partitions = $[];
/**
* bnc #468922 - problem with longtime running the parsing a huge number of disks
* map<string,map> target map try to minimalize calling Storage::GetTargetMap()
*
*/
map<string,map> target_map = $[];
/**
* mapping all devices udev-name to kernel name
* importnat for init fucntion of perl-Bootloader
*/
global map<string, string> all_devices = $[];
/**
* Storage locked
*/
boolean storage_initialized = false;
/**
* device mapping between real devices and multipath
*/
global map<string,string> multipath_mapping = $[];
// FIXME: it is ugly hack because y2-storage doesn't known
// to indicate that it finish (create partitions) proposed partitioning of disk in installation
/** bnc#594482 - grub config not using uuid
* Indicate if storage already finish partitioning of disk
* if some partition includes any keyword "create"
* the value is the first found partition with flag "create" e.g. /dev/sda2
* empty string means all partitions are created
*/
string proposed_partition = "";
/** bnc#594482 - grub config not using uuid
* 0 - if all devices in variable all_devices are created.
* 1 - if partition with flag "create" was found in MapDevices()
* 2 - if proposed_partition was created or flag "create" was deleted
* by y2-storage. the value is set in CheckProposedPartition ()
*/
integer all_devices_created = 0;
/** bnc#594482 - grub config not using uuid
* 0 - if all devices in variable all_partitions and all_disks are created
* 1 - if partition with flag "create" was found in MapDevices()
* 2 - if proposed_partition was created or flag "create" was deleted
* by y2-storage. the value is set in CheckProposedPartition ()
*/
integer all_partitions_created = 0;
/**
* mountpoints for perl-Bootloader
*/
global map<string,any> mountpoints = $[];
/**
* list of all partitions for perl-Bootloader
*/
global list<list> partinfo = [];
/**
* information about MD arrays for perl-Bootloader
*/
global map<string,list<string> > md_info = $[];
/**
* Flag indicates that bios_id_missing in disk
* true if missing false if at least one disk has bios_id
*/
global boolean bois_id_missing = true;
/**
* device mapping between Linux and firmware
*/
global map<string,string> device_mapping = $[];
/**
* string sepresenting device name of /boot partition
* same as RootPartitionDevice if no separate /boot partition
*/
global string BootPartitionDevice = "";
/**
* string representing device name of / partition
*/
global string RootPartitionDevice = "";
/**
* string representing device name of extended partition
*/
global string ExtendedPartitionDevice = "";
/**
* list of installed floppy devices
*/
global list<string> floppy_devices = nil;
/** FATE#305008: Failover boot configurations for md arrays with redundancy
* list <string> includes physical disks used for md raid
*/
global list <string> md_physical_disks = [];
/** FATE #302219 - Use and choose persistent device names for disk devices
* Function prepare maps with mapping disks and partitions by uuid, id, path
* and label.
*
*/
void MapDevices()
{
string dev_by_something = "";
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
foreach (string k, map v, devices,
{
// map disk by uuid
if ((v["uuid"]:nil != "") && (v["uuid"]:nil != nil))
{
dev_by_something = "/dev/disk/by-uuid/"+v["uuid"]:"";
all_devices[dev_by_something]= k;
}
// map disk by path
if ((v["path"]:nil != "") && (v["path"]:nil != nil))
{
dev_by_something = "/dev/disk/by-path/"+v["path"]:"";
all_devices[dev_by_something]= k;
}
// map disk by id
if ((v["udev_id"]:nil != nil) && (v["udev_id", 0]:nil != ""))
{
dev_by_something = "/dev/disk/by-id/"+v["udev_id", 0]:"";
all_devices[dev_by_something]= k;
// bnc #534905 - yast2 bootloader 2.18.15-1.1 damages /etc/grub.conf
if (size(v["udev_id"]:[]) == 2 )
{
dev_by_something = "/dev/disk/by-id/"+v["udev_id", 1]:"";
all_devices[dev_by_something]= k;
}
}
// map partitions from disk...
foreach (map p, (list<map>)(v["partitions"]:[]),
{
// bnc#594482 - grub config not using uuid
// if there is "not created" partition and flag for "it" is not set
if ((p["create"]:nil == true) && (Mode::installation()))
{
if (proposed_partition == "")
proposed_partition = p["device"]:"";
all_devices_created = 1;
}
// map partition by uuid
if ((p["uuid"]:nil != "") && (p["uuid"]:nil != nil))
{
dev_by_something = "/dev/disk/by-uuid/"+p["uuid"]:"";
all_devices[dev_by_something]= p["device"]:"";
}
// map partition by path
if ((p["path"]:nil != "") && (p["path"]:nil != nil))
{
dev_by_something = "/dev/disk/by-path/"+p["path"]:"";
all_devices[dev_by_something]= p["device"]:"";
}
// map partition by label
if ((p["label"]:nil != "") && (p["label"]:nil != nil))
{
dev_by_something = "/dev/disk/by-label/"+p["label"]:"";
all_devices[dev_by_something]= p["device"]:"";
}
// map disk by id
if ((p["udev_id"]:nil != nil) && (p["udev_id", 0]:nil != ""))
{
dev_by_something = "/dev/disk/by-id/"+p["udev_id", 0]:"";
all_devices[dev_by_something]= p["device"]:"";
// bnc #534905 - yast2 bootloader 2.18.15-1.1 damages /etc/grub.conf
if (size(p["udev_id"]:[]) == 2 )
{
dev_by_something = "/dev/disk/by-id/"+p["udev_id", 1]:"";
all_devices[dev_by_something]= p["device"]:"";
}
}
}); // end of foreach (map p, (list<map>)(v["partitions"]:[]),
}); // end of foreach (string k, map v, devices,
if (Mode::installation() && all_devices_created == 2)
{
all_devices_created = 0;
y2milestone("set status for all_devices to \"created\"");
}
y2debug("device name mapping to kernel names: %1", all_devices);
}
/** FATE #302219 - Use and choose persistent device names for disk devices
* Converts a "/dev/disk/by-" device name to the corresponding kernel
* device name, if a mapping for this name can be found in the map from
* yast2-storage. If the given device name is not a "/dev/disk/by-" device
* name, it is left unchanged. Also, if the information about the device
* name cannot be found in the target map from yast2-storage, the device
* name is left unchanged.
*
* @param dev string device name
* @return string kernel device name
*/
global define string MountByDev2Dev(string dev)
{
y2milestone ("MountByDev2Dev: %1", dev);
if (!regexpmatch(dev, "^/dev/disk/by-"))
return dev;
string ret = dev;
// check if it is device name by id
if (haskey(all_devices, dev))
ret = all_devices[dev]:"";
y2milestone("Device %1 was converted to: %2", dev, ret);
return ret;
}
/** bnc#594482 - grub config not using uuid
* Function check if proposed_partition still includes
* flag "create"
*
* @param map<string,map> tm
* @return true if partition is still not created
*/
boolean CheckProposedPartition (map<string,map> tm)
{
boolean ret = true;
if (!Mode::installation())
{
y2debug("Skip CheckProposedPartition() -> it is not running installation");
return false;
}
if (size(tm) == 0 || proposed_partition == "")
{
y2debug("proposed_partition is empty: %1", proposed_partition);
return false;
}
map dp = Storage::GetDiskPartition (proposed_partition);
string disk = dp["disk"]:"";
list <map> partitions = (list<map>)tm[disk,"partitions"]:[];
foreach (map p, partitions,
{
if (p["device"]:"" == proposed_partition)
{
if (p["create"]:nil != true)
{
proposed_partition = "";
y2milestone("proposed_partition is already created: %1", p);
if (all_devices_created == 1)
all_devices_created = 2;
if (all_partitions_created == 1)
all_partitions_created = 2;
ret = false;
} else {
y2milestone("proposed_partition: %1 is NOT created", proposed_partition);
}
break;
}
});
return ret;
}
/** bnc#594482 - grub config not using uuid
* Check if it is necessary rebuild all_devices
*
* @return true -> rebuild all_devices
*/
boolean RebuildMapDevices()
{
boolean ret = false;
boolean ret_CheckProposedPartition = CheckProposedPartition((map<string,map>)Storage::GetTargetMap());
if (!ret_CheckProposedPartition && all_devices_created == 2)
return true;
return ret;
}
/**
* Init and fullfil internal data for perl-Bootloader
*
* @return true if init reset/fullfil data or false and used cached data
*/
global boolean InitMapDevices ()
{
boolean ret = false;
if (disk_change_time_InitBootloader != Storage::GetTargetChangeTime() || RebuildMapDevices())
{
y2milestone("Init internal data from storage");
MapDevices();
disk_change_time_InitBootloader = Storage::GetTargetChangeTime();
ret = true;
}
return ret;
}
/** bnc#594482 - grub config not using uuid
* Check if it is necessary rebuild all_partitions and all_disks
*
* @return true -> rebuild all_partitions and all_disks
*/
boolean RebuilMapAllPartitions()
{
boolean ret = false;
boolean ret_CheckProposedPartition = CheckProposedPartition((map<string,map>)Storage::GetTargetMap());
if (!ret_CheckProposedPartition && all_partitions_created == 2)
return true;
return ret;
}
/**
* bnc #468922 - problem with longtime running the parsing a huge number of disks
* Function initialize all_partitions only if storage change
* partitioning of disk
* true if init all_partitions
*/
boolean MapAllPartitions()
{
boolean ret = false;
if ((disk_change_time_MapAllPartitions != Storage::GetTargetChangeTime())
|| (size(all_partitions) < 1) || (size(target_map) < 1)
|| RebuilMapAllPartitions())
{
// save last change time from storage for MapAllPartitions()
disk_change_time_MapAllPartitions = Storage::GetTargetChangeTime();
all_partitions = $[];
target_map = $[];
// get target map
target_map = (map<string,map>)Storage::GetTargetMap();
// map all partitions
foreach (string k, map v, target_map,
{
foreach (map p, (list<map>)(v["partitions"]:[]),
{
// bnc#594482 - grub config not using uuid
// if there is "not created" partition and flag for "it" is not set
if ((p["create"]:nil == true) && (Mode::installation()))
{
if (proposed_partition == "")
proposed_partition = p["device"]:"";
all_partitions_created = 1;
}
all_partitions[p["device"]:""] = p;
});
});
ret = true;
}
if (Mode::installation() && all_partitions_created == 2)
{
all_partitions_created = 0;
y2milestone("set status for all_partitions to \"created\"");
}
return ret;
}
/** FATE #302219 - Use and choose persistent device names for disk devices
* Converts a device name to the corresponding device name it should be
* mounted by, according to the "mountby" setting for the device from
* yast2-storage. As a safeguard against problems, if the "mountby" device
* name does not exist in the information from yast2-storage, it will
* fallback to the "kernel name" ("/dev/sdXY").
*
* @param dev string device name
* @return string device name according to "mountby"
*/
global define string Dev2MountByDev(string dev)
{
string tmp_dev = MountByDev2Dev(dev);
y2milestone ("Dev2MountByDev: %1 as kernel device name: %2", dev, tmp_dev);
// add all_partitions to partitions
if (MapAllPartitions())
y2milestone("Init all_partitions was done");
map<string,map> partitions = all_partitions;
map<string,map> devices = target_map;
// (`id,`uuid,`path,`device,`label)
symbol by_mount = nil;
// bnc#458018 accept different mount-by for partition
// created by user
if (!Arch::ppc())
{
by_mount = Storage::GetDefaultMountBy();
if (haskey(partitions, tmp_dev))
{
symbol partition_mount_by = (symbol) partitions[tmp_dev, "mountby"]:nil;
if ((partition_mount_by != nil))
by_mount = partition_mount_by;
}
} else {
by_mount = `id;
}
y2milestone("Mount-by: %1", by_mount);
string ret = tmp_dev;
switch(by_mount)
{
// convert to udev_id
case (`id):
// partitions
if ((partitions[tmp_dev, "udev_id"]:nil != nil) && (partitions[tmp_dev, "udev_id", 0]:nil != ""))
{
ret = sformat ("/dev/disk/by-id/%1", partitions[tmp_dev, "udev_id", 0]:"");
y2milestone ("Device name: %1 is converted to udev id: %2", tmp_dev, ret);
return ret;
}
// disks
if ((devices[tmp_dev, "udev_id"]:nil != nil) && (devices[tmp_dev, "udev_id", 0]:nil != ""))
{
ret = sformat ("/dev/disk/by-id/%1", devices[tmp_dev, "udev_id", 0]:"");
y2milestone ("Device name: %1 is converted to udev id: %2", tmp_dev, ret);
return ret;
}
break;
// convert to uuid
case (`uuid):
// partitions
if ((partitions[tmp_dev, "uuid"]:nil != nil) && (partitions[tmp_dev, "uuid"]:nil != ""))
{
ret = sformat ("/dev/disk/by-uuid/%1", partitions[tmp_dev, "uuid"]:"");
y2milestone ("Device name: %1 is converted to uuid: %2", tmp_dev, ret);
return ret;
}
// disks
if ((devices[tmp_dev, "uuid"]:nil != nil) && (devices[tmp_dev, "uuid"]:nil != ""))
{
ret = sformat ("/dev/disk/by-uuid/%1", devices[tmp_dev, "uuid"]:"");
y2milestone ("Device name: %1 is converted to uuid: %2", tmp_dev, ret);
return ret;
}
break;
// convert to udev_path
case (`path):
// partitions
if ((partitions[tmp_dev, "udev_path"]:nil != nil) && (partitions[tmp_dev, "udev_path"]:nil != ""))
{
ret = sformat ("/dev/disk/by-path/%1", partitions[tmp_dev, "udev_path"]:"");
y2milestone ("Device name: %1 is converted to udev path: %2", tmp_dev, ret);
return ret;
}
// disks
if ((devices[tmp_dev, "udev_path"]:nil != nil) && (devices[tmp_dev, "udev_path"]:nil != ""))
{
ret = sformat ("/dev/disk/by-path/%1", devices[tmp_dev, "udev_path"]:"");
y2milestone ("Device name: %1 is converted to udev path: %2", tmp_dev, ret);
return ret;
}
break;
// convert to label
case (`label):
// partitions
if ((partitions[tmp_dev, "label"]:nil != nil) && (partitions[tmp_dev, "label"]:nil != ""))
{
ret = sformat ("/dev/disk/by-label/%1", partitions[tmp_dev, "label"]:"");
y2milestone ("Device name: %1 is converted to label: %2", tmp_dev, ret);
return ret;
}
// disks
y2milestone ("Disk doesn't support labels - name: %1 is converted to label: %2", tmp_dev, ret);
return ret;
break;
// convert to device or uknown type
default:
y2warning ("Convert %1 to `device or unknwn type, result: %2", tmp_dev, ret);
return ret;
break;
}
return ret;
}
/** bnc #447591, 438243, 448110 multipath wrong device map
* Function maps real devices to multipath e.g.
* "/dev/sda/" : "/dev/mapper/SATA_ST3120813AS_3LS0CD7M"
*
* @return map <string, string> mapping real disk to multipath
*/
// FIXME grub only
map <string, string> mapRealDevicesToMultipath()
{
map <string, string> ret = $[];
map<string, map> tm = (map<string,map>)Storage::GetTargetMap ();
integer num_of_real_disk = 0;
foreach (string disk, map disk_info, tm,
{
if (disk_info["type"]:nil == `CT_DMMULTIPATH)
{
list <string> devices = (list <string>) disk_info["devices"]:[];
if (size(devices) > 0)
{
foreach(string d, devices,
{
ret[d]=disk;
});
}
}
if (disk_info["type"]:nil == `CT_DISK)
num_of_real_disk = num_of_real_disk +1;
if (disk_info["bios_id"]:nil != nil)
bois_id_missing = false;
});
if (num_of_real_disk == 1)
bois_id_missing = false;
return ret;
}
/**
* Check if function was called or storage change
* partitionig of disk. It is usefull fo using cached data
* about disk. Data is send to perl-Bootloader and it includes
* info about partitions, multi path and md-raid
*
* @return false if it is posible use cached data
*/
boolean checkCallingDiskInfo ()
{
boolean ret = false;
// fix for problem with unintialized storage library in AutoYaST mode
// bnc #464090
if (Mode::config() && (!storage_initialized))
{
storage_initialized = true;
y2milestone("Init storage library in yast2-bootloader");
Storage::InitLibstorage(true);
}
if ((disk_change_time_checkCallingDiskInfo != Storage::GetTargetChangeTime())
|| (size(partinfo) < 1))
{
// save last change time from storage
disk_change_time_checkCallingDiskInfo = Storage::GetTargetChangeTime();
y2milestone("disk was changed by storage or partinfo is empty: %1", size(partinfo));
y2milestone("generate partinfo, md_info, mountpoints and multipath_mapping");
ret = true;
} else {
ret = false;
y2milestone("Skip genarating partinfo, md_info, mountpoints and multipath_mapping");
}
return ret;
}
/**
* Function init data for perl-Bootloader about disk
* It means fullfil md_info, multipath_mapping, partinfo
* and mountpoints
*/
global void InitDiskInfo()
{
if (checkCallingDiskInfo())
{
// delete variables for perl-Bootloader
md_info = $[];
multipath_mapping = $[];
partinfo = [];
mountpoints = $[];
map<string,map> tm = (map<string,map>)Storage::GetTargetMap ();
multipath_mapping = mapRealDevicesToMultipath();
mountpoints = mapmap (string k, list v,
(map<string,list>)Storage::GetMountPoints (),
{
// detect all raid1 md devices and mark them in md_info
any device = v[0]:nil;
if (v[3]:"" == "raid1")
md_info[(string)device]=[];
return $[ k : device ];
});
mountpoints = filter (string k, any v, mountpoints, {
string tmpdir = (string)SCR::Read (.target.tmpdir);
integer tmp_sz = size (tmpdir);
return is (v, string) && substring (k, 0, tmp_sz) != tmpdir;
});
y2milestone ("Detected mountpoints: %1", mountpoints);
list<list<list> > pi = maplist (string disk, map info, tm,
{
if (info["type"]:`CT_UNKNOWN==`CT_LVM)
return [];
if (info["type"]:`CT_UNKNOWN==`CT_EVMS)
return [];
list partitions = info["partitions"]:[];
list<list> parts = maplist (map p, (list<map>)partitions, {
string raid = "";
if( p["used_by_type"]:`UB_NONE==`UB_MD )
raid = p["used_by_device"]:"";
string device = p["device"]:"";
// We only pass along RAID1 devices as all other causes
// severe breakage in the bootloader stack
if (raid != "") {
if (haskey (md_info, raid)) {
list<string> members = md_info[raid]:[];
members = add (members, device);
md_info[raid] = members;
}
}
any nr = p["nr"]:nil;
if (nr == nil)
nr = 0;
string nr_str = sformat ("%1", nr);
return [ device, disk, nr_str, tostring(p["fsid"]:0),
p["fstype"]:"unknown", tostring(p["type"]:nil),
tostring(p["region", 0]:0), tostring(p["region", 1]:0)
];
});
return parts;
});
partinfo = flatten (pi);
partinfo = filter (list p, partinfo, {return p != nil && p != [];});
list<list> partinfo_mountby = [];
// adding moundby (by-id) via user preference
foreach(list partition, partinfo, {
list tmp =[];
string mount_by = BootStorage::Dev2MountByDev(tostring(partition[0]:""));
if (mount_by != tostring(partition[0]:""))
{
tmp = add(partition, mount_by);
} else {
tmp = partition;
}
partinfo_mountby = add(partinfo_mountby, tmp);
});
// y2milestone("added mountby: %1", partinfo_mountby);
partinfo = partinfo_mountby;
}
}
/*** helper functions for ProposeDeviceMap: ***/
/**
* Returns true if any device from list devices is in device_mapping
* marked as hd0.
*/
boolean isHd0(list<string> devices)
{
boolean ret = false;
foreach (string value, devices,
{
if (device_mapping[value]:"" == "hd0"){
ret = true;
}
});
return ret;
}
/**
* Returns first key from mapping associated with value.
* Example:
* map = $[ "a" : "1",
* "b" : "2",
* "c" : "3",
* "d" : "2"];
* getDeviceFromMapping("1", map) -> "a"
* getDeviceFromMapping("2", map) -> "b"
*/
string getKey(string value, map<string, string> mapping)
{
string ret = "";
foreach (string key, string val, mapping,
{
if (value == val){
ret = key;
return;
}
});
return ret;
}
/**
* This function changes order of devices in device_mapping.
* All devices listed in bad_devices are maped to "hdN" are moved to the end
* (with changed number N). And second step is putting device with boot partition
* on top (i.e. device_mapping[dev_with_boot] = "hd0").
*
* Example:
* device_mapping = $[ "/dev/sda" : "hd0",
* "/dev/sdb" : "hd1",
* "/dev/sdc" : "hd2",
* "/dev/sdd" : "hd3",
* "/dev/sde" : "hd4" ];
* bad_devices = [ "/dev/sda", "/dev/sdc" ];
*
* changeOrderInDeviceMapping(bad_devices);
* // after call, device_mapping is:
* device_mapping -> $[ "/dev/sda" : "hd3",
* "/dev/sdb" : "hd0",
* "/dev/sdc" : "hd4",
* "/dev/sdd" : "hd1",
* "/dev/sde" : "hd2" ];
*/
void changeOrderInDeviceMapping(list<string> bad_devices)
{
integer cur_id = 0;
list keys = [];
string value = "";
string tmp = "";
string tmp2 = "";
// get keys from device_mapping, it's not possible to use foreach over keys and values
// of device_mapping directly, because during the loop device_mapping is changing.
foreach (string key, string value, device_mapping, { keys = add(keys, key); });
// put bad_devices at bottom
foreach (string key, (list<string>)keys,
{
value = device_mapping[key]:"";
// if device is mapped on hdX and this device is _not_ in bad_devices
if (substring(value, 0, 2) == "hd"
&& !contains(bad_devices, key)){
// get device name of mapped on "hd"+cur_id
tmp = getKey("hd" + tostring(cur_id), device_mapping);
// swap tmp and key devices (swap their mapping)
device_mapping[tmp] = value;
device_mapping[key] = "hd" + tostring(cur_id);
cur_id = cur_id + 1;
}
});
}
/**
* Check if MD raid is build on disks not on paritions
* @param devices - list of devices from MD raid
* @param tm - unfiltered target map
* @return - true if MD RAID is build on disks (not on partitions)
*/
boolean checkMDRaidDevices(list<string> devices, map<string,map> tm)
{
boolean ret = true;
foreach (string key, devices, {
if (key != "" && ret)
{
if (tm[key]:nil != nil)
ret = true;
else
ret = false;
}
});
return ret;
}
/**
* Function check if disk is in list of devices
* @param string disk
* @param list<string> list of devices
* @return true if success
*/
boolean isDiskInList (string disk, list<string> devices)
{
boolean ret = false;
foreach(string dev, devices,
{
if (dev == disk)
{
ret = true;
break;
}
});
return ret;
}
/**
* Check if disk is in MDRaid it means completed disk is used in RAID
* @param disk (/dev/sda)
* @param tm - target map
* @return - true if disk (not only part of disk) is in MDRAID
*/
boolean isDiskInMDRaid (string disk, map<string,map> tm)
{
boolean ret = false;
foreach( string dev, map d_info, tm,
{
if (d_info["type"]:nil == `CT_MDPART)
ret = isDiskInList(disk, d_info["devices"]:[]);
if (ret)
break;
});
return ret;
}
/*** helper functions END ***/
/**
* Generate device map proposal, store it in internal variables.
*
* FATE #302075:
* When user is installing from USB media or any non IDE disk or bios simply
* set any non IDE disk as first and user is not installing on this removable
* (non IDE) disk, the order of disks proposed by bios must be changed because
* of future remove of USB disk.
* This function must find right place for bootloader (which is most probably
* boot sector of boot partition (where /boot dir is located)) and change the
* order of disks in device map.
* This method is only heuristic because order of disks after remove of usb
* disk can't be determined by any method.
*
* Algorithm for solving problem with usb disk propsed by bios as hd0:
* if usbDiskDevice == hd0 && BootDevice != usbDiskDevice:
* change order of disks in device_mappings to have BootDevice as hd0
*/
// FIXME: remove that function from here, as it is grub only
// NOTE: there is a local copy in routines/grub/misc.ycp now
global void ProposeDeviceMap () {
list<string> usb_disks = []; // contains those usb removable disks
device_mapping = $[];
multipath_mapping = $[];
map<string,map> targetMap = $[];
if (Mode::config ())
y2milestone ("Skipping device map proposing in Config mode");
else
targetMap = (map<string,map>)Storage::GetTargetMap();
// filter out non-disk devices
targetMap = filter (string k, map v, targetMap, {
return v["type"]:`CT_UNKNOWN==`CT_DMRAID || v["type"]:`CT_UNKNOWN==`CT_DISK
|| v["type"]:`CT_UNKNOWN==`CT_DMMULTIPATH ||
(v["type"]:`CT_UNKNOWN==`CT_MDPART && checkMDRaidDevices(v["devices"]:[], targetMap));
});
// filter out members of BIOS RAIDs and multipath devices
targetMap = filter (string k, map v, targetMap, {
return v["used_by_type"]:nil != `UB_DMRAID && v["used_by_type"]:nil != `UB_DMMULTIPATH&&
((v["used_by_type"]:nil == `UB_MDPART) ? (!isDiskInMDRaid(k,targetMap)):true);
});
y2milestone ("Target map: %1", targetMap);
// add devices with known bios_id
// collect BIOS IDs which are used
map ids = $[];
foreach (string target_dev, map target, targetMap, {
string bios_id = target["bios_id"]:"";
if (bios_id != "")
{
integer index = tointeger (bios_id) - tointeger ("0x80");
string grub_dev = sformat ("hd%1", index);
// FATE #303548 - doesn't add disk with same bios_id with different name (multipath machine)
if (! ids[index]:false)
{
device_mapping[target_dev] = grub_dev;
ids[index] = true;
}
}
});
// and guess other devices
// don't use already used BIOS IDs
foreach (string target_dev, map target, targetMap, {
string bios_id = target["bios_id"]:"";
if (bios_id == "")
{
integer index = 0;
while (ids[index]:false)
index = index + 1;
string grub_dev = sformat ("hd%1", index);
device_mapping[target_dev] = grub_dev;
ids[index] = true;
}
});
// Fill usb_disks list with usb removable devices.
//
// It's not easy to determine how to identify removable usb devices. Now
// it tests if driver of device is usb-storage. If you find better
// algorithm how to find removable usb devices, put it here into foreach
// to apply this algorithm on all devices.
foreach (string target_dev, map target, targetMap,
{
string driver = target["driver"]:"";
if (driver == "usb-storage"){
usb_disks = add(usb_disks, target_dev);
}
});
y2milestone("Found usb discs: %1", usb_disks);
// change order in device_mapping if usb disk is hd0
// (FATE #302075)
if (isHd0(usb_disks) && BootPartitionDevice != getKey("hd0", device_mapping)){
y2milestone ("Detected device mapping: %1", device_mapping);
y2milestone("Changing order in device mapping needed...");
changeOrderInDeviceMapping(usb_disks);
}
if (size(device_mapping) == 1)
bois_id_missing = false;
if (StorageDevices::FloppyPresent())
device_mapping[StorageDevices::FloppyDevice()] = "fd0";
y2milestone ("Detected device mapping: %1", device_mapping);
multipath_mapping = mapRealDevicesToMultipath();
y2milestone ("Detected multipath mapping: %1", multipath_mapping);
}
/**
* Get the order of disks according to BIOS mapping
* @return a list of all disks in the order BIOS sees them
*/
global list<string> DisksOrder () {
if (device_mapping == nil || size (device_mapping) == 0)
{
ProposeDeviceMap ();
}
map<string,string> devmap_rev = mapmap (string k, string v, device_mapping,
{
return $[ v : k ];
});
devmap_rev = filter (string k, string v, devmap_rev, {
return substring (k, 0, 2) == "hd";
});
list<string> order = maplist (string k, string v, devmap_rev, {
return v;
});
return order;
}
/**
* Function remap device map to device name (/dev/sda)
* or to label (ufo_disk)
* @param map<string,string> device map
* @return map<string,string> new device map
*/
global map<string,string> remapDeviceMap(map<string,string> device_map)
{
symbol by_mount = nil;
if (Arch::ppc())
by_mount = `id;
else
by_mount = Storage::GetDefaultMountBy();
//by_mount = `id;
if (by_mount == `label)
return device_map;
map<string,string> ret = $[];
// convert device names in device map to the device names by device or label
ret = mapmap (string k , string v, device_mapping,
{
return $[BootStorage::MountByDev2Dev(k) : v];
});
return ret;
}
/**
* Get the list of installed floppy drives
* @return a list of floppy devices
*/
global list<string> getFloppyDevices ()
{
if (floppy_devices == nil)
{
list<map> floppies = (list<map>) SCR::Read (.probe.floppy);
floppies = filter (map f, floppies,
``(f["model"]:"Floppy Disk" == "Floppy Disk"));
floppy_devices = maplist (map f, floppies,
``(f["dev_name"]:""));
floppy_devices = filter (string f, floppy_devices, ``(f != ""));
}
return floppy_devices;
}
/**
* Returns list of partitions with "mount by" hints. Goes through the list
* of partitions passed as a parameter and creates a list of partitions with
* hints according to the current partitioning requested from
* yast2-storage. To be used in a combobox or menu.
*
* @param parts_to_get list<string> partitions to list
* @return a list of strings containing a partition name and a hint (if applicable)
*/
global define list<string> getHintedPartitionList(list<string> parts_to_get)
{
y2milestone ("getHintedPartitionList: %1", parts_to_get);
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
// make a map: "/dev/hda1" -> info_map_for_this_partition
map<string,map> partitions = $[];
foreach (string k, map v, devices, ``{
foreach (map p, (list<map>)(v["partitions"]:[]), ``{
partitions[p["device"]:""] = p;
});
});
y2milestone ("getHintedPartitionList: partitions %1", partitions);
symbol mountby = `device;
list<string> ret = maplist (string dev, parts_to_get, ``{
mountby = (symbol) partitions[dev, "mountby"]:nil;
if ( mountby == `uuid )
{
return sformat ("%1 (mount by UUID: %2)", dev,
partitions[dev, "uuid"]:nil != nil ?
partitions[dev, "uuid"]:"" :
"<UUID to be created later during format>");
} else if ( mountby == `label )
{
return sformat ("%1 (mount by LABEL: %2)", dev, partitions[dev, "label"]:"");
} else if ( mountby == `id )
{
return sformat ("%1 (mount by ID: %2)", dev, partitions[dev, "udev_id", 0]:"");
} else if ( mountby == `path )
{
return sformat ("%1 (mount by PATH: %2)", dev, partitions[dev, "udev_path"]:"");
} else if ( mountby == nil || mountby == `device )
{
return dev;
}
});
y2milestone ("getHintedPartitionList: ret %1", ret);
return ret;
}
/**
* Returns list of partitions. Requests current partitioning from
* yast2-storage and creates list of partition for combobox, menu or other
* purpose.
* @param type symbol
* `boot - for bootloader installation
* `root - for kernel root
* `boot_other - for bootable partitions of other systems
* `all - all partitions
* `parts_old - all partitions, except those what will be created
* during isntallation
* `deleted - all partitions deleted in current proposal
* `kept - all partitions that won't be deleted, new created or formatted
* `destroyed - all partition which are new, deleted or formatted
* @return a list of strings
*/
global define list<string> getPartitionList(symbol type, string bl)
{
y2milestone ("getPartitionList: %1", type);
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
list<map> partitions = [];
foreach (string k, map v, devices, ``{
if ((type == `boot) && (bl == "grub"))
{
// check if device is in device map
if (haskey(device_mapping, k) || (haskey(device_mapping, Dev2MountByDev(k))))
partitions = (list<map>)merge (partitions, (list<map>)(v["partitions"]:[]));
} else {
partitions = (list<map>)merge (partitions, (list<map>)(v["partitions"]:[]));
}
});
list<string> floppies = getFloppyDevices ();
devices = filter (string k, map v, devices,
``(v["type"]:`CT_UNKNOWN != `CT_LVM));
if (type == `boot || type == `boot_other)
{
devices = filter (string k, map v, devices, {
if ((v["type"]:`CT_UNKNOWN == `CT_DISK) ||
(v["type"]:`CT_UNKNOWN == `CT_DMRAID))
return true;
else
return false;
});
}
list<string> all_disks = (list<string>) maplist (string k, map v, devices,
{
return k;
});
if (type == `deleted)
return maplist (map x, filter (map p, partitions, ``(p["delete"]:false)),
``{return x["device"]:"";
});
else if (type == `destroyed)
{
return maplist (map x, filter (map p, partitions, {
return p["delete"]:false || p["format"]:false
|| p["create"]:false;
}),
{
return x["device"]:"";
});
}
partitions = filter (map p, partitions, ``(! p["delete"]:false));
// filter out disk which are not in device map
if ((bl == "grub") && (type == `boot))
all_disks = (list<string>) filter(string k, all_disks, {
if (haskey(device_mapping, k) || (haskey(device_mapping, Dev2MountByDev(k))))
return true;
else
return false;
});
list<string> ret = all_disks;
if (type == `boot_other || type == `root || type == `parts_old
|| type == `kept)
ret = [];
if (type == `boot)
{
partitions = filter (map p, partitions,
``(p["type"]:`primary == `primary
|| p["type"]:`primary == `extended
|| p["type"]:`primary == `logical
|| p["type"]:`primary == `sw_raid)
);
// FIXME this checking is performed on 3 places, one function should
// be developed for it
partitions = filter (map p, partitions, {
symbol fs = (symbol)(p["used_fs"]:p["detected_fs"]:nil);
if (fs == `xfs)
return false;
return true;
});
}
else if (type == `root)
{
partitions = filter (map p, partitions,
``(p["type"]:`primary != `extended));
}
else if (type == `parts_old)
{
partitions = filter (map p, partitions, ``(! p["create"]:false));
}
else if (type == `kept)
{
partitions = filter (map p, partitions, {
return ! (p["create"]:false || p["format"]:false);
});
}
if (type != `all && type != `parts_old && type != `kept)
{
partitions = filter (map p, partitions,
``(p["fstype"]:"" != "Linux swap"));
}
if (type == `boot)
{
partitions = filter (map p, partitions,
``(p["fstype"]:"" == "Linux native"
|| p["fstype"]:"" == "Extended"
|| p["fstype"]:"" == "Linux RAID"
|| tolower(p["fstype"]:"") == "md raid"
|| p["fstype"]:"" == "DM RAID"));
}
list<string>partition_names
= maplist (map p, partitions, ``((string)(p["device"]:"")));
partition_names = filter (string p, partition_names, ``(p != ""));
partition_names = (list<string>)merge (partition_names, floppies);
ret = (list<string>)union (ret, partition_names);
ret = (list<string>)toset (ret);
return (list<string>)ret;
}
/** FATE#305008: Failover boot configurations for md arrays with redundancy
* Check if devices has same partition number and if they are from different disks
*
* @param list <string> list of devices
* @return boolean true on success
*/
boolean checkDifferentDisks (list <string> devices)
{
boolean ret = false;
list <string> disks = [];
string no_partition = "";
foreach(string dev, devices,
{
map p_dev = Storage::GetDiskPartition (dev);
if (!contains(disks, p_dev["disk"]:""))
disks=add(disks,p_dev["disk"]:"");
else
{
y2milestone("Same disk for md array -> disable synchronize md arrays");
break;
}
// add disk from partition to md_physical_disks
if (!contains(md_physical_disks, p_dev["disk"]:""))
md_physical_disks = add(md_physical_disks,p_dev["disk"]:"");
string no_p = p_dev["nr"]:"";
if (no_p == "")
{
y2error("Wrong number of partition: %1 from Storage::GetDiskPartition: %2", dev, p_dev);
break;
}
if (no_partition == "")
no_partition = no_p;
else
if (no_partition == no_p)
ret = true;
else
y2milestone("Different number of partitions -> disable synchronize md arrays");
});
y2milestone("checkDifferentDisks for devices: %1 return: %2",devices, ret);
return ret;
}
/** FATE#305008: Failover boot configurations for md arrays with redundancy
* Check if device are build from 2 partitions with same number but from different disks
*
* @param map<string,map> tm taregte map from storage
* @param string device (md device)
* @return true if device is from 2 partisions with same number and different disks
*/
boolean checkMDDevices(map<string,map> tm, string device)
{
boolean ret = false;
map <string, any> tm_dm = (map<string, any>) tm["/dev/md"]:$[];
md_physical_disks = [];
// find partitions in target map
foreach(map p, tm_dm["partitions"]:[],
{
if (p["device"]:"" == device)
{
if (p["raid_type"]:"" == "raid1")
{
list <string> p_devices = p["devices"]:[];
if (size(p_devices) == 2)
ret = checkDifferentDisks(p_devices);
else
y2milestone("Device: %1 doesn't contain 2 partitions: %2", device, p_devices);
} else {
y2milestone ("Device: %1 is not on raid1: %2",device, p["raid_type"]:"");
}
}
});
if ((size(md_physical_disks) != 2) || (contains(md_physical_disks, "")))
y2milestone("device: %1 is based on md_physical_disks: %2 is not valid for enable redundancy",
device, md_physical_disks);
if (ret)
y2milestone("device: %1 is based on md_physical_disks: %2 is valid for enable redundancy",
device, md_physical_disks);
return ret;
}
/** FATE#305008: Failover boot configurations for md arrays with redundancy
* Function check partitions and set redundancy available if
* partitioning of disk allows it.
* It means if md array is based on 2 partitions with same number but 2 different disks
* E.g. /dev/md0 is from /dev/sda1 and /dev/sb1 and /dev/md0 is "/"
* There is possible only boot from MBR (GRUB not generic boot code)
*
* @return boolean true on success
*/
global boolean checkMDSettings ()
{
boolean ret = false;
map<string,map> tm = (map<string,map>)Storage::GetTargetMap();
if (!haskey(tm, "/dev/md"))
{
y2milestone("Doesn't include md raid");
return ret;
}
list<string> boot_devices = [];
if ((BootPartitionDevice != "") && (BootPartitionDevice != nil))
boot_devices = add(boot_devices, BootPartitionDevice);
if ((BootPartitionDevice != RootPartitionDevice) && (RootPartitionDevice != "")
&& (BootPartitionDevice != nil))
boot_devices = add(boot_devices, RootPartitionDevice);
if ((ExtendedPartitionDevice != "")&& (ExtendedPartitionDevice != nil))
boot_devices = add(boot_devices, ExtendedPartitionDevice);
y2milestone("Devices for analyse of redundacy md array: %1", boot_devices);
foreach(string dev, boot_devices,
{
ret = checkMDDevices(tm, dev);
if (!ret)
{
y2milestone("Skip enable redundancy of md arrays");
break;
}
});
return ret;
}
/** FATE#305008: Failover boot configurations for md arrays with redundancy
* Function prapare disks for synchronizing of md array
*
* @return string includes disks separatet by ","
*/
global string addMDSettingsToGlobals()
{
string ret = "";
if (checkMDSettings())
ret = mergestring(md_physical_disks,",");
return ret;
}
}
ACC SHELL 2018