ACC SHELL
/**
* File: modules/NetworkInterfaces.ycp
* Package: Network configuration
* Summary: Interface manipulation (/etc/sysconfig/network/ifcfg-*)
* Authors: Michal Svec <msvec@suse.cz>
*
* $Id: NetworkInterfaces.ycp 43062 2007-12-13 16:12:26Z mzugec $
*
* The new sysconfig naming is interface (eg. eth0) vs. device
* (eg. NE2000 card), but historically yast has called them device
* vs. module.
*/
{
module "NetworkInterfaces";
textdomain "base";
import "Arch";
import "Map";
import "Mode";
import "Netmask";
import "String";
/**
* False suppresses tones of logs 'NetworkInterfaces.ycp:ABC Check(eth,id-00:aa:bb:cc:dd:ee,)'
*/
global boolean report_every_check = true;
/**
* Current device identifier
* @example eth0, eth1:blah, lo, ...
* Add, Edit and Delete copy the requested device info (via Select)
* to Name and Current,
* Commit puts it back
*/
global string Name = "";
// value is not just string, can be a map for aliases
typedef map<string, any> ifcfg_t;
typedef map<string, map<string, ifcfg_t> > devices_t;
/**
* Current device information
* @example $["BOOTPROTO":"dhcp", "STARTMODE":"auto"]
*/
global ifcfg_t Current = $[];
/**
* Interface information:
* Devices[string type, string id] is a map with the contents of
* ifcfg-<i>type</i>-<i>id</i>. Separating type from id is useful because
* the type determines the fields of the interface file.
* Multiple addresses for an interface are nested maps
* [type, id, "_aliases", aid]
* @see Read
*/
devices_t Devices = $[];
/**
* Devices information
* @see Read
*/
devices_t OriginalDevices = $[];
/**
* Deleted devices
*/
list<string> Deleted = [];
/**
* True if devices are already read
*/
boolean initialized = false;
/**
* Which operation is pending?
*/
/* global */ symbol operation = nil;
// FIXME: used in lan/address.ycp (#17346) -> "global"
/**
* Predefined network card regular expressions
*/
global map<string,string> CardRegex = $[
"netcard" : "arc|ath|bnep|ci|ctc|dummy|bond|escon|eth|fddi|ficon|hsi|qeth|lcs|iucv|myri|tr|usb|wlan|xp|vlan|br|tun|tap|ib",
"modem" : "ppp|modem",
"isdn" : "isdn|ippp",
"dsl" : "dsl",
/* other: irlan|lo|plip|... */
];
// define string HotplugRegex(list<string> devs);
/**
* Supported hotplug types
*/
list<string> HotplugTypes = [ "pcmcia", "usb"/*, "pci" */];
/**
* Create a list of hot-pluggable device names for the given devices
*/
define string HotplugRegex(list<string> devs) {
string ret = "";
foreach(string dev, devs, {
foreach(string hot, HotplugTypes, {
ret = ret + "|" + dev + "-" + hot + "|" + dev + "-" + hot + "-";
});
});
return ret;
}
/**
* Predefined network device regular expressions
*/
global map<string,string> DeviceRegex = $[
/* device types */
"netcard" : CardRegex["netcard"]:"" + HotplugRegex(["ath", "eth", "tr", "wlan"]) + "|usb-usb|usb-usb-",
"modem" : CardRegex["modem"]:"",
"isdn" : CardRegex["isdn"]:"" + HotplugRegex(["isdn", "ippp"]),
"dsl" : CardRegex["dsl"]:"",
/* device groups */
"dialup" : CardRegex["modem"]:"" + "|" + CardRegex["dsl"]:"" + "|" + CardRegex["isdn"]:"",
];
/**
* Types in order from fastest to slowest.
* @see FastestRegexps
*/
map<integer,string> FastestTypes = $[
1 : "dsl",
2 : "isdn",
3 : "modem",
4 : "netcard"
];
/**
* @see Push
*/
map stack = $[];
// -------------------- components of configuration names --------------------
/**
* A single character used to separate alias id
*/
string alias_separator = "#";
/**
* ifcfg name = type + id + alias_id
* If id is numeric, it is not separated from type, otherwise separated by "-"
* Id may be empty
* Alias_id, if nonempty, is separated by alias_separator
*/
string ifcfg_name_regex =
"^" +
// ip6: #48696
"(ip6tnl|mip6mnha|[" + String::CAlpha() + "]+)" + "-?" +
"([^" + alias_separator + "]*)" + alias_separator + "?" +
"(.*)" +
"$";
string ifcfg_part (string ifcfg, string part) {
if (regexpmatch (ifcfg, ifcfg_name_regex) != true)
{
return "";
}
string ret = regexpsub (ifcfg, ifcfg_name_regex, "\\" + part);
return (ret == nil)? "": ret;
}
/**
* Return a device type
* @param dev device
* @return device type
* @example device_type("eth1") -> "eth"
* @example device_type("eth-pcmcia-0") -> "eth-pcmcia"
*/
global string device_type (string dev) {
return ifcfg_part (dev, "1");
}
global string GetType(string dev) {
string type = ifcfg_part (dev, "1");
foreach(string dev_type,map confs, Devices, {
if (haskey(confs, dev)){
string int_type = confs[dev, "INTERFACETYPE"]:"";
if (size(confs[dev, "TUNNEL"]:"")>0) int_type = confs[dev, "TUNNEL"]:"";
if(size(int_type)>0) type = int_type;
}
});
return type;
}
/**
* Return device type in human readable form :-)
* @param dev device
* @return device type
* @example GetDeviceType(eth-bus-pci-0000:01:07.0) -> "Network Card"
* @example GetDeviceType(modem0) -> "Modem"
*/
global string GetDeviceType(string dev) {
if (regexpmatch(dev,"^" + DeviceRegex["netcard"]:"")) {
return(_("Network Card"));
}
else if (regexpmatch(dev,"^" + DeviceRegex["modem"]:"")) {
return(_("Modem"));
}
else if (regexpmatch(dev,"^" + DeviceRegex["isdn"]:"")) {
return(_("ISDN"));
}
else if (regexpmatch(dev,"^" + DeviceRegex["dsl"]:"")) {
return(_("DSL"));
}
else return(_("Unknown"));
}
/**
* Return a device number
* @param dev device
* @return device number
* @example device_num("eth1") -> "1"
* @example device_num("lo") -> ""
*/
global string device_num (string dev) {
return ifcfg_part (dev, "2");
}
/**
* Return a device alias number
* @param dev device
* @return alias number
* @example alias_num("eth1#2") -> "2"
* @example alias_num("eth1#blah") -> "blah"
*/
global string alias_num (string dev) {
return ifcfg_part (dev, "3");
}
/**
* Create a device name from its type and number
* @param typ device type
* @param num device number
* @return device name
* @example device_name("eth", "1") -> "eth1"
* @example device_name("lo", "") -> "lo"
*/
global string device_name (string typ, string num) {
if(typ == nil || typ == "") {
y2error("wrong type: %1", typ);
return nil;
}
if(num == nil /* || num < 0 */) {
y2error("wrong number: %1", num);
return nil;
}
/* FIXME: devname
if(IsHotplug(typ) && num != "") return sformat("%1-%2", typ, num);
return sformat("%1%2", typ, num);
*/
if(regexpmatch(num, "^[0-9]*$"))
return sformat("%1%2", typ, num);
return sformat("%1-%2", typ, num);
}
/**
* Create a alias name from its type and numbers
* @param typ device type
* @param num device number
* @param anum alias number
* @return alias name
* @example alias_name("eth", "1", "2") -> "eth1#2"
*/
global string alias_name (string typ, string num, string anum) {
if(typ == nil || typ == "") {
y2error("wrong type: %1", typ);
return nil;
}
if(num == nil /* || num < 0 */) {
y2error("wrong number: %1", num);
return nil;
}
if(anum == nil || anum == "") {
y2error("wrong alias number: %1", anum);
return nil;
}
return sformat("%1#%2", device_name(typ, num), anum);
}
/**
* Test hotplugability of a device
* @param type device type
* @return true if hotpluggable
*/
global boolean IsHotplug (string type) {
if(type == "" || type == nil) return false;
if(regexpmatch(type, "(pcmcia|usb|pci)$")) return true;
return false;
}
/**
* Return matching inteface for this hardware ID (uses getcfg-interface)
* @param dev unique device string
* return interface name
* @example MatchInterface("eth-id-00:01:DE:AD:BE:EF") -> "eth0"
*/
/*
global string MatchInterface(string dev) {
string cmd = "getcfg-interface " + dev;
map dn =(map) SCR::Execute(.target.bash_output, cmd);
string devname = deletechars(dn["stdout"]:"", "\n");
return devname;
}
*/
/**
* Test whether device is connected (Link:up)
* The info is taken from sysfs
* @param dev unique device string
* @return true if connected
*/
global boolean IsConnected(string dev) {
if (!Mode::testsuite()) {
// string iface = MatchInterface(dev);
string cmd = "cat /sys/class/net/" + dev + "/carrier";
map ret = (map) SCR::Execute(.target.bash_output, cmd);
y2milestone("Sysfs returned %1", ret);
return ( deletechars( ret["stdout"]:"", "\n") == "1" ? true : false );
}
else
//Assume all devices are connected in testsuite mode
return true;
}
/**
* Return real type of the device (incl. PCMCIA, USB, ...)
* @param type basic device type
* @param hotplug hot plug type
* @return real type
* @example RealType("eth", "usb") -> "eth-usb"
*/
global string RealType (string type, string hotplug) {
y2debug("type=%1", type);
if(type == "" || type == nil) {
y2error("Wrong type: %1", type);
return "eth";
}
if(hotplug == "" || hotplug == nil)
return type;
string realtype = type + "-" + hotplug;
y2debug("realtype=%1", realtype);
return realtype;
}
// ---------------------------------------------------------------------------
/**
* STARTMODE: onboot, on and boot are aliases for auto
*/
global map<string, any> CanonicalizeStartmode (map<string, any> ifcfg) {
map <string, string> canonicalize_startmode = $[
"on": "auto",
"boot": "auto",
"onboot": "auto",
];
string startmode = ifcfg["STARTMODE"]:"";
ifcfg["STARTMODE"] = canonicalize_startmode[startmode]:startmode;
return ifcfg;
}
/**
* Canonicalize netmask data (#46885)
* Sysconfig allows:
* IPADDR=10.0.0.1/8
* IPADDR=10.0.0.1 PREFIXLEN=8
* IPADDR=10.0.0.1 NETMASK=255.0.0.0
* (IPADDR overrides PREFIXLEN, NETMASK used only if prefix length unspecified)
* If prefix length and NETMASK are unspecified, 32 is implied.
* Canonicalize it to
* IPADDR=10.0.0.1 PREFIXLEN= NETMASK=255.0.0.0
* @param ifcfg a map containing IPADDR and possibly NETMASK, PREFIXLEN
* and possibly other fields
* @return the map with IPADDR, NETMASK adjusted; PREFIXLEN ""
* others unchanged. If IPADDR is empty, return the original.
*/
global map<string, any> CanonicalizeIP (map<string, any> ifcfg) {
if (ifcfg == nil)
{
return nil;
}
list<string> ip_and_prefix = splitstring (ifcfg["IPADDR"]:"", "/");
string ipaddr = ip_and_prefix[0]:"";
if (ipaddr == "") // DHCP or inconsistent
{
return ifcfg;
}
string prefixlen = ip_and_prefix[1]:"";
if (prefixlen == "")
{
prefixlen = ifcfg["PREFIXLEN"]:"";
}
if (prefixlen == "")
{
prefixlen = tostring (Netmask::ToBits (ifcfg["NETMASK"]:""));
}
// Now we have ipaddr and prefixlen
// Let's compute the rest
string netmask = Netmask::FromBits (tointeger (prefixlen));
map<string, any> ret = ifcfg;
ret["IPADDR"] = ipaddr;
ret["PREFIXLEN"] = prefixlen;
ret["NETMASK"] = netmask;
return ret;
}
list<string> SensitiveFields = [
"WIRELESS_WPA_PASSWORD",
"WIRELESS_WPA_PSK",
// the unnumbered one should be empty but just in case
"WIRELESS_KEY",
"WIRELESS_KEY_0",
"WIRELESS_KEY_1",
"WIRELESS_KEY_2",
"WIRELESS_KEY_3",
];
/**
* Conceal secret information, such as WEP keys, so that the output
* can be passed to y2log and bugzilla.
* @param ifcfg one ifcfg
* @return ifcfg with secret fields masked out
*/
global map ConcealSecrets1 (map<string, any> ifcfg) {
if (ifcfg == nil)
{
return nil;
}
map out = mapmap (string k, any v, ifcfg, {
if (contains (SensitiveFields, k) && v != "")
{
v = "CONCEALED";
}
return $[k: v];
});
return out;
}
/**
* Conceal secret information, such as WEP keys, so that the output
* can be passed to y2log and bugzilla. (#65741)
* @param devs a two-level map of ifcfgs like Devices
* @return ifcfgs with secret fields masked out
*/
global map ConcealSecrets (map devs) {
if (devs == nil)
{
return nil;
}
map out = mapmap (string t, map<string, map<string, any> > tdevs,
(map<string, map<string, map<string, any> > >) devs, {
map tout = mapmap (string id, map<string, any> ifcfg, tdevs, {
return $[id: ConcealSecrets1 (ifcfg)];
});
return $[t: tout];
});
return out;
}
/**
* Read devices from files
* @return true if sucess
*/
global define boolean Read() {
// initialized = true; // FIXME
if(initialized == true) return true;
Devices = $[];
/* Variables which could be suffixed and thus duplicated */
list Locals = [ "IPADDR", "REMOTE_IPADDR", "NETMASK", "PREFIXLEN",
"BROADCAST", "SCOPE", "LABEL", "IP_OPTIONS" ];
/* preparation */
list<string> allfiles = SCR::Dir(.network.section);
if(allfiles == nil) allfiles = [];
list<string> devices = filter(string file, allfiles, {
return !regexpmatch(file, "[~]");
});
y2debug("devices=%1", devices);
/* FIXME: devname
devices = filter(string d, devices, {
return regexpmatch(d, "[a-z][a-z-]*[0-9]*");
});
y2debug("devices=%1", devices);
*/
/* Read devices */
maplist(string d, devices, {
string devtype = GetType(d);
// string devnum = "";
// if(regexpmatch(d, "[a-z][a-z-]*[0-9]+"))
// devnum = sformat("%1", device_num(d));
// y2debug("devnum=%1", devnum);
map<string, ifcfg_t> dev = Devices[devtype]:$[];
string pth = ".network.value.\"" + d + "\"";
y2debug("pth=%1", pth);
list<string> values = SCR::Dir(topath(pth));
y2debug("values=%1", values);
map<string, any> config = $[];
maplist(string val, values, {
string item = (string) SCR::Read(topath(pth + "." + val));
y2debug("item=%1", item);
if(item == nil) return;
/* No underscore '_' -> global */
/* Also temporarily standard globals */
if(find(val, "_") < 0 || contains(Locals, val)) {
config[val] = item;
return;
}
/* Try to strip _suffix */
string v = substring(val, 0, findlastof(val, "_"));
string s = substring(val, findlastof(val, "_"));
if(size(s) > 1) s = substring(s, 1);
y2milestone("%1:%2:%3", val, v, s);
/* Global */
if(!contains(Locals, v))
config[val] = item;
/* Local */
else {
map _aliases = config["_aliases"]:$[];
map suf = _aliases[s]:$[];
suf[v] = item;
_aliases[s] = suf;
config["_aliases"] = _aliases;
}
});
y2milestone("config=%1", ConcealSecrets1 (config));
// canonicalize, #46885
map <string, map> caliases = mapmap (string a, map<string, any> c, (map<string,map<string, any> >)config["_aliases"]:$[], {
return $[a: CanonicalizeIP (c)];
});
if (caliases != $[]) // unconditionally?
{
config["_aliases"] = caliases;
}
config = CanonicalizeIP (config);
config = CanonicalizeStartmode (config);
if (size(config["INTERFACETYPE"]:"")>0) {
y2milestone("option INTERFACETYPE changes devtype from %1 to %2", devtype, config["INTERFACETYPE"]:"");
devtype=config["INTERFACETYPE"]:"";
dev = Devices[devtype]:$[];
}
/*
if(haskey(dev, devnum)) {
y2error("device already present: %1", devnum);
return;
}
*/
dev[d] = config;
Devices[devtype] = dev;
});
y2debug("Devices=%1", Devices);
OriginalDevices = Devices;
initialized = true;
return true;
}
/**
* re-read all settings again from system
* for creating new proposal from scratch (#170558)
*/
global boolean CleanCacheRead(){
initialized = false;
return Read();
}
/**
*/
define map<string,map> Filter(map<string,map> devices, string devregex) {
if(devices == nil || devregex == nil || devregex == "")
return devices;
string regex = "^(" + DeviceRegex[devregex]:devregex + ")[0-9]*$";
y2debug("regex=%1", regex);
devices = filter(string file, map devmap, devices, {
return regexpmatch(file, regex) == true;
});
y2debug("devices=%1", devices);
return devices;
}
/**
* Used in BuildSummary, BuildOverview
*/
global map<string,map> FilterDevices (string devregex) {
return Filter (Devices, devregex);
}
/**
*/
define map<string,map> FilterNOT(map<string,map> devices, string devregex) {
if(devices == nil || devregex == nil || devregex == "")
return $[];
string regex = "^(" + DeviceRegex[devregex]:devregex + ")[0-9]*$";
y2debug("regex=%1", regex);
devices = filter(string file, map devmap, devices, {
return regexpmatch(file, regex) != true;
});
y2debug("devices=%1", devices);
return devices;
}
/**
* For the NAME field, filter out characters that will case problems
* for the shell or the ini agent. (#72164)
* It should be done in more places but this field is most susceptible.
* @param s a string
* @return s with some characters removed, esp. the single quote
*/
string ShellSafe (string s) {
s = filterchars (s, String::CGraph () + " ");
return deletechars (s, "'");
}
/**
* SCR::Write (p, ShellSafe (s)) and if s had to be changed,
* log the _path_ (not the value, for privacy).
* @see ShellSafe
* @param p SCR path
* @param s value
* @return success
*/
boolean ShellSafeWrite (path p, string s) {
string safe_s = ShellSafe (s);
if (safe_s != s)
{
y2milestone ("Changed: %1", p);
}
return SCR::Write (p, safe_s);
}
/**
* Write devices to files
* @param devregex regular expression for the device type
* @return true if success
* @example NetworkDevice::Write("eth|tr");
*/
global define boolean Write(string devregex) {
y2milestone("Writing configuration");
y2debug("Devices=%1", Devices);
y2debug("Deleted=%1", Deleted);
map Devs = Filter(Devices, devregex);
map OriginalDevs = Filter(OriginalDevices, devregex);
y2milestone("OriginalDevs=%1", ConcealSecrets (OriginalDevs));
y2milestone("Devs=%1", ConcealSecrets (Devs));
/* Check for changes */
if(Devs == OriginalDevs) {
y2milestone("No changes to %1 devices -> nothing to write", devregex);
return true;
}
/* remove deleted devices */
y2milestone("Deleted=%1", Deleted);
foreach(string d, Deleted, {
// if(!haskey(OriginalDevs, d)) return;
string anum = alias_num (d);
if (anum == "")
{
/* delete config file */
path p = add (.network.section, d);
y2debug("deleting: %1", p);
SCR::Write(p, nil);
}
else
{
string typ = device_type (d);
string num = device_num (d);
string dev = device_name (typ, num);
path base = add (.network.value, dev);
// look in OriginalDevs because we need to catch all variables
// of the alias
foreach (string key, any dummy, OriginalDevs[typ, dev, "_aliases", anum]:$[], {
path p = add (base, key + "_" + anum);
y2debug ("deleting: %1", p);
SCR::Write (p, nil);
});
}
});
/* Devices with chmod=0600 */
list<string> chmod = [];
/* write all devices */
maplist(string typ, map<string,map<string,any> > devsmap, (map<string, map<string, map<string, any> > >) Devs, {
maplist(string config, map<string,any> devmap, devsmap, {
if (devmap == OriginalDevs[typ,config]:$[])
return;
/* write sysconfig */
string p = ".network.value.\"" + config + "\".";
if (size(devmap["IPADDR"]:"")>0 && size(devmap["NETMASK"]:"")>0){
devmap["IPADDR"] = sformat("%1/%2", devmap["IPADDR"]:"", Netmask::ToBits(devmap["NETMASK"]:""));
devmap=remove(devmap, "NETMASK");
//TODO : delete NETMASK from config file
} else {
if (size(devmap["IPADDR"]:"")>0 && size(devmap["PREFIXLEN"]:"")>0){
devmap["IPADDR"] = sformat("%1/%2", devmap["IPADDR"]:"", devmap["PREFIXLEN"]:"");
devmap=remove(devmap, "PREFIXLEN");
//TODO : delete PREFIXLEN from config file
}
}
/* write all keys to config */
maplist(string k, (list<string>) Map::Keys(devmap), {
/* Write aliases */
if(k == "_aliases") {
maplist(string anum, map<string,string> amap, devmap[k]:$[], {
// Normally defaulting the label would be done
// when creating the map, not here when
// writing, but we create it in 2 ways so it's
// better here. Actually it does not work because
// the edit dialog nukes LABEL :-(
// boolean seen_label = false;
if (size(amap["IPADDR"]:"")>0 && size(amap["NETMASK"]:"")>0){
amap["IPADDR"] = sformat("%1/%2", amap["IPADDR"]:"", Netmask::ToBits(amap["NETMASK"]:""));
amap=remove(amap, "NETMASK");
//TODO : delete NETMASK from config file
} else {
if (size(amap["IPADDR"]:"")>0 && size(amap["PREFIXLEN"]:"")>0){
amap["IPADDR"] = sformat("%1/%2", amap["IPADDR"]:"", amap["PREFIXLEN"]:"");
amap=remove(amap, "PREFIXLEN");
//TODO : delete PREFIXLEN from config file
}
}
maplist(string ak, string av, amap, {
string akk = ak + "_" + anum;
ShellSafeWrite (topath (p + akk), av);
// seen_label = seen_label || ak == "LABEL";
});
/*
if (!seen_label)
{
ShellSafeWrite (topath (p + ("LABEL_" + anum)), anum);
}
*/
});
}
/* Write regular keys */
else
ShellSafeWrite (topath (p + k), devmap[k]:"");
});
/* update libhd unique number * /
// FIXME: move it somewhere else: hardware
string unq = devmap["UNIQUE"]:"";
if(unq != "") SCR::Write(.probe.status.configured, unq, `yes);
*/
/* 0600 if contains encryption key (#24842) */
boolean has_key = find (string k, SensitiveFields,
``( devmap[k]:"" != "" )) != nil;
string file = "/etc/sysconfig/network/ifcfg-" + config;
y2debug("Permission change: %1, %2", has_key, file);
if(has_key) {
y2debug("CHANGED");
chmod = add(chmod, file);
}
});
});
/* Finish him */
SCR::Write(.network, nil);
/* CHMOD */
y2debug("chmod=%1", chmod);
maplist(string file, chmod, {
y2debug("changing: %1", file);
SCR::Execute(.target.bash, "/bin/chmod 0600 " + file);
});
// Deleted = [];
// OriginalDevices = Devices;
// Cannot do it because we have written only part of Devices.
// This module should be rewritten to objects.
return true;
}
/**
* Import data
* @param settings settings to be imported
* @return true on success
*/
global define boolean Import(string devregex, map<string,map> devices) {
map Devs = FilterNOT(Devices, devregex);
y2debug("Devs=%1", Devs);
devices = mapmap(string typ, map devsmap, devices, {
return $[typ: mapmap(string num, map<string, any> config, (map<string,map<string, any> >) devsmap, {
config = CanonicalizeIP (config);
config = CanonicalizeStartmode (config);
return $[num: config];
})];
});
Devices = (devices_t) union(Devs, devices);
OriginalDevices = nil;
return true;
}
/**
* Return supported network device types (for type netcard)
* for this hardware
*/
global list<string> GetDeviceTypes(){
list<string> dev_types = ["eth", "tr", "vlan", "br", "tun", "tap", "ib", "bond"];
if(Arch::s390 ()) dev_types = (list<string>) merge(dev_types, [ "hsi", "ctc", "escon", "ficon", "iucv", "qeth", "lcs"]);
else dev_types = (list<string>) merge(dev_types, [ "arc", "bnep", "dummy", "fddi", "myri", "usb", "wlan" ]);
if(Arch::ia64 ()) dev_types = add(dev_types, "xp");
foreach(string device,dev_types, {
if (!contains(splitstring(DeviceRegex["netcard"]:"", "|"), device)) y2error("%1 is not contained in DeviceRegex[\"netcard\"]", device);
});
return dev_types;
}
/**
* Return textual device type
* @param type device type
* @param type description type
* @return textual form of device type
* @example GetDevTypeDescription("eth", false) -> "Ethernet"
* @example GetDevTypeDescription("eth", true) -> "Ethernet Network Card"
*/
global string GetDevTypeDescription(string type, boolean longdescr){
if(issubstring(type, "#"))
/* Device type label */
// This is what used to be Virtual Interface (eth0:1).
// In our data model, additional addresses for an interface
// are represented as its sub-interfaces.
// And also we frequently confuse "device" and "interface"
// :-(
return _("Additional Address");
map device_types = $[
/* Device type label */
"arc" : [ _("ARCnet"), _("ARCnet Network Card") ],
/* Device type label */
"atm" : [ _("ATM"), _("Asynchronous Transfer Mode (ATM)") ],
/* Device type label */
"bnep" : [ _("Bluetooth"), _("Bluetooth Connection") ],
/* Device type label */
"bond" : [ _("Bond"), _("Bond Network") ],
/* Device type label */
"ci" : [ _("CLAW"), _("Common Link Access for Workstation (CLAW)") ],
/* Device type label */
"contr" : [ _("ISDN"), _("ISDN Card") ],
/* Device type label */
"ctc" : [ _("CTC"), _("Channel to Channel Interface (CTC)") ],
/* Device type label */
"dsl" : [ _("DSL"), _("DSL Connection") ],
/* Device type label */
"dummy" : [ _("Dummy"), _("Dummy Network Device") ],
/* Device type label */
"escon" : [ _("ESCON"), _("Enterprise System Connector (ESCON)") ],
/* Device type label */
"eth" : [ _("Ethernet"), _("Ethernet Network Card") ],
/* Device type label */
"fddi" : [ _("FDDI"), _("FDDI Network Card") ],
/* Device type label */
"ficon" : [ _("FICON"), _("Fiberchannel System Connector (FICON)") ],
/* Device type label */
"hippi" : [ _("HIPPI"), _("HIgh Performance Parallel Interface (HIPPI)") ],
/* Device type label */
"hsi" : [ _("Hipersockets"), _("Hipersockets Interface (HSI)") ],
/* Device type label */
"ippp" : [ _("ISDN"), _("ISDN Connection") ],
/* Device type label */
"irlan" : [ _("IrDA"), _("Infrared Network Device") ],
/* Device type label */
"irda" : [ _("IrDA"), _("Infrared Device") ],
/* Device type label */
"isdn" : [ _("ISDN"), _("ISDN Connection") ],
/* Device type label */
"iucv" : [ _("IUCV"), _("Inter User Communication Vehicle (IUCV)") ],
/* Device type label */
"lcs" : [ _("OSA LCS"), _("OSA LCS Network Card") ],
/* Device type label */
"lo" : [ _("Loopback"), _("Loopback Device") ],
/* Device type label */
"modem" : [ _("Modem"), _("Modem") ],
/* Device type label */
"myri" : [ _("Myrinet"), _("Myrinet Network Card") ],
/* Device type label */
"net" : [ _("ISDN"), _("ISDN Connection") ],
/* Device type label */
"plip" : [ _("Parallel Line"), _("Parallel Line Connection") ],
/* Device type label */
"ppp" : [ _("Modem"), _("Modem") ],
/* Device type label */
"qeth" : [ _("QETH"), _("OSA-Express or QDIO Device (QETH)") ],
/* Device type label */
"sit" : [ _("IPv6-in-IPv4"), _("IPv6-in-IPv4 Encapsulation Device") ],
/* Device type label */
"slip" : [ _("Serial Line"), _("Serial Line Connection") ],
/* Device type label */
"tr" : [ _("Token Ring"), _("Token Ring Network Card") ],
/* Device type label */
"usb" : [ _("USB"), _("USB Network Device") ],
/* Device type label */
"vmnet" : [ _("VMWare"), _("VMWare Network Device") ],
/* Device type label */
"wlan" : [ _("Wireless"), _("Wireless Network Card") ],
/* Device type label */
"xp" : [ _("XPNET"), _("XP Network") ],
/* Device type label */
"vlan" : [ _("VLAN"), _("Virtual LAN") ],
/* Device type label */
"br" : [ _("Bridge"), _("Network Bridge") ],
/* Device type label */
"tun" : [ _("TUN"), _("Network TUNnel") ],
/* Device type label */
"tap" : [ _("TAP"), _("Network TAP") ],
/* Device type label */
"ib" : [ _("InfiniBand"), _("InfiniBand Device") ],
];
if(haskey(device_types, type)) return device_types[type, (longdescr==true) ? 1 : 0]:"";
string type1 = String::FirstChunk (type, "-");
if(haskey(device_types, type1))
return device_types[type1, (longdescr==true) ? 1 : 0]:"";
y2error("Unknown type: %1", type);
return type;
}
/**
* Export data
* @return dumped settings (later acceptable by Import())
*/
global define map<string,map> Export(string devregex) {
map Devs = Filter(Devices, devregex);
y2debug("Devs=%1", Devs);
return (map<string,map>) Devs;
}
/**
* Were the devices changed?
* @return true if modified
*/
global define boolean Modified(string devregex) {
map Devs = Filter(Devices, devregex);
map OriginalDevs = Filter(OriginalDevices, devregex);
y2debug("OriginalDevs=%1", OriginalDevs);
y2debug("Devs=%1", Devs);
return Devs == OriginalDevs;
}
global define list<string> GetFreeDevices(string type, integer num) {
y2debug("Devices=%1", Devices);
y2debug("type,num=%1,%2", type, num);
y2debug("Devices[%1]=%2", type, Devices[type]:$[]);
list curdevs = [];
foreach(string dev, (list<string>)Map::Keys(Devices[type]:$[]), {
if(issubstring(dev, type)) dev=device_num(dev);
curdevs=add(curdevs, dev);
});
integer i = 0;
integer count = 0;
list<string> ret = [];
/* Hotpluggable devices */
if(IsHotplug(type) && !contains(curdevs, "")) {
y2debug("Added simple hotplug device");
count = count + 1;
ret = add(ret, "");
}
/* Remaining numbered devices */
while(count < num) {
string ii = sformat("%1", i);
if(!contains(curdevs, ii)) {
ret = add(ret, ii);
count = count + 1;
}
i = i + 1;
}
y2debug("Free devices=%1", ret);
return ret;
}
/**
* Compute free devices
* @param type device type
* @param num how many free devices return
* @return num of free devices
* @example GetFreeDevices("eth", 2) -> [ 1, 2 ]
*/
global define list GetFreeDevicesOld(string type, integer num) {
y2debug("Devices=%1", Devices);
y2debug("type,num=%1,%2", type, num);
y2debug("Devices[%1]=%2", type, Devices[type]:$[]);
list curdevs = Map::Keys(Devices[type]:$[]);
y2debug("curdevs=%1", curdevs);
integer i = 0;
integer count = 0;
list ret = [];
/* Hotpluggable devices */
if(IsHotplug(type) && !contains(curdevs, "")) {
y2debug("Added simple hotplug device");
count = count + 1;
ret = add(ret, "");
}
/* Remaining numbered devices */
while(count < num) {
string ii = sformat("%1", i);
if(!contains(curdevs, ii)) {
ret = add(ret, ii);
count = count + 1;
}
i = i + 1;
}
y2debug("Free devices=%1", ret);
return ret;
}
/**
* Return free device
* @param type device type
* @return free device
* @example GetFreeDevice("eth") -> "1"
*/
global define string GetFreeDevice(string type) {
y2debug("type=%1", type);
list <string> freedevs = GetFreeDevices(type, 1);
string ret = (string) freedevs[0]:nil;
if(ret == nil) y2error("Free device location error: %1", ret);
y2debug("Free device=%1", ret);
return ret;
}
/**
* Check presence of the device (alias)
* @param dev device identifier
* @return true if device is present
*/
global define boolean Check(string dev) {
y2debug("Check(%1)", dev);
string typ = GetType(dev);
// string num = device_num(dev);
// string anum = alias_num(dev);
if (report_every_check) y2milestone("Check(%1)", dev);
if(!haskey(Devices, typ)) return false;
map devsmap = Devices[typ]:$[];
if(!haskey(devsmap, dev)) return false;
/* FIXME NI: not needed?
Name = dev;
Current = (map) eval(devsmap[num]:$[]);
*/
/*
if(anum != "") {
map devmap = devsmap[num]:$[];
map amap = devmap["_aliases"]:$[];
if(!haskey(amap, anum))
return false;
// FIXME NI: not needed?
// Current = (map) eval(amap[anum]:$[]);
// alias = anum;
}
*/
y2debug("Check passed");
return true;
}
/**
* Select the given device
* @param device to select ("" for new device, default values)
* @return true if success
*/
global define boolean Select(string name) {
Name = "";
Current = $[];
y2debug("name=%1", name);
if(name != "" && !Check(name)) {
y2error("No such device: %1", name);
return false;
}
Name = name;
// FIXME NI: Current = Devices[device_type(Name), device_num(Name)]:$[];
// may be fixed already. or not: #39236
string t = GetType(Name);
Current = Devices[t, Name]:$[];
string a = alias_num(Name);
if(a != nil && a != "") Current = Current["_aliases", a]:$[];
if(Current == $[]) {
/* Default device map */
Current = $[
/* FIXME: remaining items */
];
}
y2debug("Name=%1", Name);
y2debug("Current=%1", Current);
return true;
}
/**
* Add a new device
* @return true if success
*/
global define boolean Add() {
operation = nil;
if(Select("") != true) return false;
operation = `add;
return true;
}
/**
* Edit the given device
* @param dev device to edit
* @return true if success
*/
global define boolean Edit(string name) {
operation = nil;
if(Select(name) != true) return false;
operation = `edit;
return true;
}
/**
* Delete the given device
* @param dev device to delete
* @return true if success
*/
global define boolean Delete(string name) {
operation = nil;
if(Select(name) != true) return false;
operation = `delete;
return true;
}
/**
* Update Devices map
* @param dev device identifier
* @param newdev new device map
* @param check if check if device already exists
* @return true if success
*/
define boolean Change2(string name, ifcfg_t newdev, boolean check) {
y2debug("Change(%1,%2,%3)", name, newdev, check);
y2debug("Devices=%1", Devices);
if(Check(name) && check) {
y2error("Device already present: %1", name);
return false;
}
string t = GetType(name);
if (name==Name) {
string int_type = Current["INTERFACETYPE"]:"";
if(size(int_type)>0) t = int_type;
}
string a = alias_num(name);
y2debug("ChangeDevice(%1)", name);
map<string, ifcfg_t> devsmap = Devices[t]:$[];
ifcfg_t devmap = devsmap[name]:$[];
map amap = devmap["_aliases"]:$[];
if(a != "") {
amap[a] = newdev;
devmap["_aliases"] = amap;
}
else
devmap = newdev;
devsmap[name] = devmap;
Devices[t] = devsmap;
y2debug("Devices=%1", Devices);
return true;
}
global boolean Delete2(string name) {
if(!Check(name)) {
y2error("Device not found: %1", name);
return false;
}
string t = GetType(name);
// string d = device_num(name);
string a = alias_num(name);
map<string, ifcfg_t> devsmap = Devices[t]:$[];
if(a != "") {
map amap = devsmap[name, "_aliases"]:$[];
amap = remove(amap, a);
devsmap[name, "_aliases"] = amap;
}
else
devsmap = remove(devsmap, name);
Devices[t] = devsmap;
// Originally this avoided errors in the log when deleting an
// interface that was not present at Read (had no ifcfg file).
// #115448: OriginalDevices is not updated after Write so
// returning to the network proposal and deleting a card would not work.
if (true ||
haskey(OriginalDevices, t) && haskey(OriginalDevices[t]:$[], name)) {
y2milestone("Deleting file: %1", name);
Deleted[size(Deleted)] = name;
}
else {
y2milestone("Not deleting file: %1", name);
y2debug("OriginalDevices=%1", OriginalDevices);
y2debug("a=%1", a);
}
return true;
}
/**
* Add the alias to the list of deleted items.
* Called when exiting from the aliases-of-device dialog.
* #48191
*/
global boolean DeleteAlias (string device, string aid) {
string alias = sformat ("%1#%2", device, aid);
y2milestone("Deleting alias: %1", alias);
Deleted[size(Deleted)] = alias;
return true;
}
global define boolean Commit() {
y2debug("Name=%1", Name);
y2debug("Current=%1", Current);
y2debug("Devices=%1", Devices);
y2debug("Deleted=%1", Deleted);
y2debug("operation=%1", operation);
if(operation == `add || operation == `edit) {
Change2(Name, Current, operation == `add);
}
else if(operation == `delete) {
Delete2(Name);
}
else {
y2error("Unknown operation: %1 (%2)", operation, Name);
return false;
}
y2debug("Devices=%1", Devices);
y2debug("Deleted=%1", Deleted);
Name = "";
Current = $[];
operation = nil;
return true;
}
global define string GetValue(string name, string key) {
if(!Select(name)) return nil;
return Current[key]:"";
}
global define boolean SetValue(string name, string key, string value) {
if(!Edit(name)) return nil;
if(key == nil || key == "" || value == nil) return false;
Current[key] = value;
return Commit();
}
/**
* get IP addres + additional IP addresses
* @param identifier for network interface
* @return list of IP addresses of selected interface
*/
global list<string> GetIP(string device){
Select(device);
list<string> ips = [ GetValue(device, "IPADDR") ];
foreach(string key, map<string, any> value, (map<string, map<string, any> >)Current["_aliases"]:$[], {
ips = add(ips, value["IPADDR"]:"");
});
return ips;
}
/**
* Locate devices of the given type and value
* @param key device key
* @param val device value
* @return list of devices with key=val
*/
global define list<string> Locate(string key, string val) {
list<string> ret = [];
maplist(string typ, map devsmap, Devices, {
maplist(string device, map devmap, (map<string,map>) devsmap, {
if(devmap[key]:"" == val) ret = add(ret, device);
});
});
return ret;
}
/**
* Locate devices of the given type and value
* @param key device key
* @param val device value
* @return list of devices with key!=val
*/
global define list<string> LocateNOT(string key, string val) {
list<string> ret = [];
maplist(string typ, map devsmap, Devices, {
maplist(string device, map devmap, (map<string,map>) devsmap, {
if(devmap[key]:"" != val) ret = add(ret, device);
});
});
return ret;
}
/**
* Check if any device is using the specified provider
* @param provider provider identification
* @return true if there is any
*/
global define boolean LocateProvider(string provider) {
list devs = Locate("PROVIDER", provider);
return size(devs) > 0;
}
/**
* Update /dev/modem symlink
* @return true if success
*/
global define boolean UpdateModemSymlink() {
boolean ret = false;
if(contains(Map::Keys(Devices), "modem")) {
list ml = Map::Keys(Devices["modem"]:$[]);
string ms = ml[0]:"0";
// map mm = Devices["modem"]:$[][ms]:$[];
map mm = Devices["modem", ms]:$[];
string mdev = mm["MODEM_DEVICE"]:"";
if(mdev != "" && mdev != "/dev/modem") {
string curlink = nil;
map m = (map) SCR::Read(.target.lstat, "/dev/modem");
if(m["islink"]:false == true)
curlink = (string) SCR::Read(.target.symlink, "/dev/modem");
if(curlink != mdev) {
SCR::Execute(.target.symlink, mdev, "/dev/modem");
ret = true;
}
}
}
return ret;
}
/**
* Clean the hotplug devices compatibility symlink,
* usually ifcfg-eth-pcmcia -> ifcfg-eth-pcmcia-0.
* @return true if success
*/
global define boolean CleanHotplugSymlink() {
list<string> types = [ "eth-pcmcia", "eth-usb", "tr-pcmcia", "tr-usb" ];
maplist(string t, types, {
string link = "/etc/sysconfig/network/ifcfg-" + t;
y2debug("link=%1", link);
map lstat = (map) SCR::Read(.target.lstat, link);
if(lstat["islink"]:false == true) {
string file = (string) SCR::Read(.target.symlink, link);
file = "/etc/sysconfig/network/" + file;
y2debug("file=%1", file);
if(SCR::Read(.target.size, file) > -1) {
y2milestone("Cleaning hotplug symlink");
y2milestone("Devices[%1]=%2", t, Devices[t]:$[]);
Devices[t] = remove(Devices[t]:$[], "");
y2milestone("Devices[%1]=%2", t, Devices[t]:$[]);
}
}
});
y2debug("Devices=%1", Devices);
return true;
}
/**
* Get devices of the given type
* @param type devices type ("" for all)
* @return list of found devices
*/
global define list<string> List(string devregex) {
list<string> ret = [];
if(devregex == "" || devregex == nil) {
maplist(string t, map d, Devices, {
maplist(string device, (list<string>) Map::Keys(d), {
ret[size(ret)] = device;
});
});
}
else {
// it's a regex for type, not the whole name
string regex = "^(" + DeviceRegex[devregex]:devregex + ")$";
maplist(string t, map d, Devices, {
if(regexpmatch(t, regex)) {
maplist(string device, (list<string>) Map::Keys(d), {
ret[size(ret)] = device;
});
}
});
}
ret = filter(string row, ret, {
if (row!=nil) return true;
y2error("Filtering out : %1", row);
return false;
});
y2milestone("List( %1 ) = %2", devregex, ret);
return ret;
}
/**
* Find the fastest available device
*/
global define string Fastest() {
string ret = "";
list<string> devices = List("");
/* Find the fastest device */
foreach(integer num, string type, FastestTypes, {
foreach(string dev, devices, {
if(ret == "" && regexpmatch(dev, "^" + DeviceRegex[type]:"" + "[0-9]*$")
&& IsConnected(dev))
ret = dev;
});
});
y2milestone("ret=%1", ret);
return ret;
}
global define string FastestType(string name) {
string ret = "";
maplist(integer num, string type, FastestTypes, {
string regex = DeviceRegex[type]:"";
if (ret == "" && regexpmatch(name, "^" + regex + "[0-9]*$"))
ret = type;
});
/*
maplist(string typ, string regex, DeviceRegex, {
if (ret == "" && regexpmatch(name, "^" + regex + "[0-9]*$"))
ret = typ;
});
*/
return ret;
}
/**
* Check if the given device has any virtual alias.
* @param dev device to be checked
* @return true if there are some aliases
*/
global define boolean HasAliases(string name) {
if(!Check(name)) {
y2error("Device not found: %1", name);
return false;
}
string t = device_type(name);
string d = device_num(name);
string a = alias_num(name);
return a == "" && Devices[t, d, "_aliases"]:$[] != $[];
}
/**
* DSL needs to save its config while the underlying network card is
* being configured.
*/
global define void Push() {
if(stack != $[]) y2error("Stack not empty: %1", stack);
stack["Name"] = Name;
stack["Current"] = Current;
stack["operation"] = operation;
y2milestone("PUSH: %1", stack);
}
global define void Pop() {
y2milestone("POP: %1", stack);
Name = stack["Name"]:"";
Current = stack["Current"]:$[];
operation = (symbol) stack["operation"]:nil;
stack = $[];
}
/**
* #46803: forbid "/" (filename), maybe also "-" (separator) "_" (escape)
*/
global string ValidCharsIfcfg () {
return String::ValidCharsFilename ();
}
/**
* list of all devices except given one by parameter dev
* also loopback is ommited
*/
global list<string> ListDevicesExcept(string dev){
list<string> devices = filter(string s, LocateNOT("DEVICE", dev), {return s!="lo";});
return devices;
}
/* EOF */
}
ACC SHELL 2018