ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/Lan.ycp

/**
 * File:	modules/Lan.ycp
 * Package:	Network configuration
 * Summary:	Network card data
 * Authors:	Michal Svec <msvec@suse.cz>
 *
 * $Id: Lan.ycp 62071 2010-05-27 12:00:38Z mzugec $
 *
 * Representation of the configuration of network cards.
 * Input and output routines.
 */

{

module "Lan";
textdomain "network";

import "Arch";
import "DNS";
import "NetHwDetection";
import "Host";
import "Hostname";
import "IP";
import "Map";
import "Mode";
import "NetworkConfig";
import "NetworkInterfaces";
import "NetworkService";
//import "NetworkStorage";
import "Package";
import "ProductFeatures";
import "Routing";
import "Progress";
import "Service";
import "String";
import "Summary";
import "SuSEFirewall4Network";
import "FileUtils";
import "PackageSystem";
import "LanItems";
import "ModuleLoading";
import "Linuxrc";
import "Stage";
import "LanUdevAuto";
import "Label";

include "network/complex.ycp";
include "network/runtime.ycp";

/*-------------*/
/* GLOBAL DATA */

// gui or cli mode
boolean gui=true;

boolean write_only = false;

/**
 * Current module information
 */
// FIXME: MOD global map Module = $[];

// propose configuration for virtual networks (bridged) ?
global boolean virt_net_proposal = nil;

/**
 * autoinstallation: if true, write_only is disabled and the network settings
 * are applied at once, like during the normal installation. #128810, #168806
 * boolean start_immediately = false;
 */

/**
 * boolean if we do automatic installation in second stage
 */

global boolean automatic_configuration=false;


// ipv6 module
global boolean ipv6=true;

/**
 * Hotplug type ("" if not hot pluggable)
 */

/**
 * Abort function
 * return boolean return true if abort
 */
global block<boolean> AbortFunction = nil;


/*------------------*/
/* GLOBAL FUNCTIONS */
/*------------------*/

/**
 * Return a modification status
 * @return true if data was modified
 */
global boolean Modified() {
    boolean ret  = LanItems::Modified() || DNS::modified || Routing::Modified() ||
	NetworkConfig::Modified();
    return ret;
}

// function for use from autoinstallation (Fate #301032)
global define boolean isAnyInterfaceDown(){
 boolean down = false;
  map<string, any> link_status = $[];
  list <string> net_devices = splitstring(
	((map<string, any>)SCR::Execute(.target.bash_output, "ls /sys/class/net/ | grep -v lo | tr '\n' ','"))["stdout"]:"", ",") ;
  net_devices = filter(string item, net_devices, {return (size(item)>0);});
  foreach(string net_dev, net_devices, {
   list<string> row = splitstring(((map<string, any>)SCR::Execute(.target.bash_output, sformat("ip address show dev %1 | grep 'inet\\|link' | sed 's/^ \\+//g'|cut -d' ' -f-2", net_dev)))["stdout"]:"", "\n");
    string tmp_mac="";
    boolean addr=false;
    foreach(string column, row, {
     list<string> tmp_col=splitstring(column, " ");
     if (size(tmp_col)<2) continue;
     if (issubstring(tmp_col[0]:"", "link/ether") ) tmp_mac = tmp_col[1]:"";
     if (issubstring(tmp_col[0]:"", "inet") && !issubstring(tmp_col[0]:"", "inet6")) addr = true;
    });
    if (size(tmp_mac)>0) link_status[tmp_mac]=addr;
    y2debug("link_status %1", link_status);
  });

    y2milestone("link_status %1", link_status);
    map <string, any> configurations =  NetworkInterfaces::FilterDevices("netcard");
    foreach(string devtype, splitstring(NetworkInterfaces::CardRegex["netcard"]:"", "|"), {
     foreach(string devname, (list<string>) Map::Keys(configurations[devtype]:$[]), {
      string mac = ((map<string, any>)SCR::Execute(.target.bash_output, sformat("cat /sys/class/net/%1/address|tr -d '\n'", devname)))["stdout"]:"";
      y2milestone("confname %1", mac);
      if (!haskey(link_status, mac)) y2error("Mac address %1 not found in map %2!", mac, link_status);
	else if (link_status[mac]:false == false) {
		y2warning("Interface with mac %1 is down!", mac);
		down = true;
	       } else y2debug("Interface with mac %1 is up", mac);
     });
    });
 return down;
}

void readIPv6(){
  ipv6=true;

 map<string, map<string, any> > methods = $[
        "builtin"  : $[
                "filelist" : ["sysctl.conf"],
                "filepath" : "/etc/",
                "regexp"   : "^[[:space:]]*(net.ipv6.conf.all.disable_ipv6)[[:space:]]*=[[:space:]]*1"
        ],
        "module" : $[
                "filelist" : ["ipv6", "50-ipv6.conf"],
                "filepath" : "/etc/modprobe.d/",
                "regexp"   : "^[[:space:]]*(install ipv6 /bin/true)"
        ]
 ];

 foreach(string which,map<string,any> method, methods, {
 list<string>  filelist = method["filelist"]:[];
 string filepath = method["filepath"]:"";
 string regexp = method["regexp"]:"";

  foreach(string file, filelist,{
    string filename=sformat("%1/%2", filepath, file);
    if (FileUtils::Exists(filename)){
      foreach(string row, splitstring((string)SCR::Read(.target.string, filename), "\n"), {
        if (size(regexptokenize(String::CutBlanks(row), regexp))>0){
           y2milestone("IPv6 is disabled by '%1' method.", which);
           ipv6=false;
        }
      });
    }
  });
 });

}

/**
 * Lan::Read (`cache) will do nothing if initialized already.
 */
boolean initialized = false;
/**
 * Read all network settings from the SCR
 * @param cache:
 *  `cache=use cached data,
 *  `nocache=reread from disk (for reproposal); TODO pass to submodules
 * @return true on success
 */
global define boolean Read (symbol cache) {

    if (cache == `cache && initialized)
    {
	y2milestone ("Using cached data");
	return true;
    }

    /* Read dialog caption */
    string caption = _("Initializing Network Configuration");
    integer steps = 9;

    integer sl = 0; /* 1000; /* TESTING */
    sleep(sl);

    if (gui) Progress::New(caption, " ", steps, [
	/* Progress stage 1/9 */
	_("Detect network devices"),
	/* Progress stage 2/9 */
	_("Read driver information"),
	/* Progress stage 3/9 - multiple devices may be present, really plural*/
	_("Read device configuration"),
	/* Progress stage 4/9 */
	_("Read network configuration"),
	/* Progress stage 5/9 */
	_("Read firewall settings"),
	/* Progress stage 6/9 */
	_("Read hostname and DNS configuration"),
	/* Progress stage 7/9 */
	_("Read installation information"),
	/* Progress stage 8/9 */
	_("Read routing configuration"),
	/* Progress stage 9/9 */
	_("Detect current status"),
    ], [], "");

    if(Abort()) return false;

    // check the environment
//    if(!Confirm::MustBeRoot()) return false;

    if(Abort()) return false;
    /* Progress step 1/9 */
    if (gui) ProgressNextStage(_("Detecting for ndiswrapper..."));
	// modprobe ndiswrapper before hwinfo when needed (#343893)
    if (!Mode::autoinst() && PackageSystem::Installed("ndiswrapper")){
	y2milestone("ndiswrapper: installed");
     if (size((list<string>)SCR::Read(.target.dir, "/etc/ndiswrapper"))>0){
	y2milestone("ndiswrapper: configuration found");
      if ((integer)SCR::Execute(.target.bash, "lsmod |grep -q ndiswrapper")!=0
		&& Popup::YesNo(_("There is a ndiswrapper configuration detected,
but the Kernel module was not modprobed.
Do you want to modprobe ndiswrapper?
"))) {
	if (ModuleLoading::Load("ndiswrapper", "", "", "", false, true)==`fail) Popup::Error(_("ndiswrapper kernel module wasn't loaded.
Check configuration manually."));
	}
     }
    }

    /* ReadHardware(""); /* TESTING */
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 2/9 */
    if (gui) ProgressNextStage(_("Detecting network devices..."));
    // FIXME: MOD // ReadModules(NetworkInterfaces::CardRegex["netcard"]:"");
    // Dont read hardware data in config mode
    if(!Mode::config ()) {
	if(!NetHwDetection::running) NetHwDetection::Start();
    }
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 3/9 - multiple devices may be present, really plural*/
    if (gui) ProgressNextStage(_("Reading device configuration..."));
     LanItems::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 4/9 */
    if (gui) ProgressNextStage(_("Reading network configuration..."));
    NetworkConfig::Read();

    readIPv6();

    sleep(sl);

    if(Abort()) return false;
    /* Progress step 5/9 */
    if (gui) ProgressNextStage(_("Reading firewall settings..."));
    boolean orig = Progress::set (false);
    SuSEFirewall4Network::Read ();
    if (gui) Progress::set (orig);
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 6/9 */
    if (gui) ProgressNextStage(_("Reading hostname and DNS configuration..."));
    DNS::Read();
    Host::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 7/9 */
    if (gui) ProgressNextStage(_("Reading installation information..."));
//    ReadInstallInf();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 8/9 */
    if (gui) ProgressNextStage(_("Reading routing configuration..."));
    Routing::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 9/9 */
    if (gui) ProgressNextStage(_("Detecting current status..."));
    NetworkService::Read ();
    sleep(sl);

    if(Abort()) return false;
    /* Final progress step */
    if (gui) ProgressNextStage(_("Finished"));
    sleep(sl);

    if(Abort()) return false;
    LanItems::modified = false;
    initialized = true;

    if (gui) Progress::Finish();

    return true;
}

/**
 * (a specialization used when a parameterless function is needed)
 * @return Read(`cache)
 */
global boolean ReadWithCache () {
    return Read (`cache);
}

global boolean ReadWithCacheNoGUI () {
 gui=false;
 return ReadWithCache();
}

global void SetIPv6(boolean status){
    if(ipv6 != status) {
	 ipv6=status;
	Popup::Warning(_("To apply this change a reboot is needed."));
         LanItems::SetModified();
    }
}

void writeIPv6(){
//  SCR::Write(.target.string, "/etc/modprobe.d/ipv6", sformat("%1install ipv6 /bin/true", ipv6?"#":""));
// uncomment to write to old place (and comment code bellow)
  SCR::Write(.target.string, "/etc/modprobe.d/50-ipv6.conf", sformat("%1install ipv6 /bin/true\n", ipv6?"#":""));
  string filename = "/etc/sysctl.conf";
  string sysctl = (string)SCR::Read(.target.string, filename);
  string sysctl_row = sformat("%1net.ipv6.conf.all.disable_ipv6 = 1", ipv6?"# ":"");
  boolean found = false; //size(regexptokenize(sysctl, "(net.ipv6.conf.all.disable_ipv6)"))>0;
  list<string> file=[];
  foreach(string row, splitstring(sysctl, "\n"), {
    if (size(regexptokenize(row, "(net.ipv6.conf.all.disable_ipv6)"))>0){
         row = sysctl_row;
         found = true;
     }
     file=add(file, row);
  });
  if (!found) file=add(file, sysctl_row);
  SCR::Write(.target.string, filename, mergestring(file, "\n"));
  SCR::Execute(.target.bash, sformat("sysctl -w net.ipv6.conf.all.disable_ipv6=%1",!ipv6?"1":"0"));
  SCR::Write(.sysconfig.windowmanager.KDE_USE_IPV6, ipv6?"yes":"no");
}


/**
 * Update the SCR according to network settings
 * @return true on success
 */
global define boolean Write() {

    y2milestone("Writing configuration");

    // Query modified flag in all components, not just LanItems - DNS,
    // Routing, NetworkConfig too in order not to discard changes made
    // outside LanItems (bnc#439235)
    if(!Modified()) {
	y2milestone("No changes to network setup -> nothing to write");
	return true;
    }

    boolean fw_is_installed = SuSEFirewall4Network::IsInstalled();

    /* Write dialog caption */
    string caption = _("Saving Network Configuration");

    integer sl = 0; /* 1000; /* TESTING */
    sleep(sl);

    list<string> step_labels = [
	/* Progress stage 2 */
	_("Write drivers information"),
	/* Progress stage 3 - multiple devices may be present,really plural*/
	_("Write device configuration"),
	/* Progress stage 4 */
	_("Write network configuration"),
	/* Progress stage 5 */
	_("Write routing configuration"),
	/* Progress stage 6 */
	_("Write hostname and DNS configuration"),
	/* Progress stage 7 */
	_("Set up network services"),
        ];
	/* Progress stage 8 */
     if (fw_is_installed)
        step_labels=add(step_labels, _("Write firewall settings"));
     /* Progress stage 9 */
     if (!write_only) step_labels = add(step_labels, _("Activate network services"));
	/* Progress stage 10 */
     step_labels=add(step_labels, _("Run SuSEconfig"));
     if (!NetworkService::IsManaged() && /*(boolean) SCR::Read(.init.scripts.exists, "smpppd") &&*/ !write_only )
	{
	 /* Progress stage 11 */
	 step_labels=add(step_labels, _("Set up smpppd"));
	}

     Progress::New(caption, " ", size(step_labels), step_labels, [], "");


    if(Abort()) return false;
    /* Progress step 2 */
    ProgressNextStage(_("Writing /etc/modprobe.conf..."));
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 3 - multiple devices may be present, really plural*/
    ProgressNextStage(_("Writing device configuration..."));
    if (!Mode::autoinst()) 
	LanItems::WriteUdevDriverRules();
    NetworkInterfaces::Write("netcard");
    // WriteDevices();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 4 */
    ProgressNextStage(_("Writing network configuration..."));
    NetworkConfig::Write();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 5 */
    ProgressNextStage(_("Writing routing configuration..."));
    boolean orig = Progress::set (false);
    Routing::Write();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 6 */
    ProgressNextStage(_("Writing hostname and DNS configuration..."));
    // write resolv.conf after change from dhcp to static (#327074)
    // reload/restart network before this to put correct resolv.conf from dhcp-backup
    DNS::Write();
    Host::EnsureHostnameResolvable();
    Host::Write();
    Progress::set (orig);

    sleep(sl);

    if(Abort()) return false;
    /* Progress step 7 */
    ProgressNextStage(_("Setting up network services..."));
    NetworkService::EnableDisable ();
    writeIPv6();
    sleep(sl);

    //Show this only if SuSEfirewall is installed
    if(fw_is_installed) {
        if(Abort()) return false;
        /* Progress step 8 */
        ProgressNextStage(_("Writing firewall settings..."));
        orig = Progress::set (false);
        SuSEFirewall4Network::Write ();
        Progress::set (orig);
        sleep(sl);
    }

    if(!write_only) {
     if(Abort()) return false;
     /* Progress step 9 */
     ProgressNextStage(_("Activating network services..."));
   // during installation export sysconfig settings into NetworkManager (bnc#433084)
   if (Mode::installation() && NetworkService::IsManaged()) y2internal("Export sysconfig settings into NetworkManager %1", SCR::Execute(.target.bash_output, "/usr/lib/NetworkManager/nm-opensuse-sysconfig-merge --connections"));

y2internal("virt_net_proposal %1", virt_net_proposal);
    if (Stage::cont() && virt_net_proposal && (Linuxrc::usessh() || Linuxrc::vnc() || Linuxrc::display_ip())) {
	UI::OpenDialog(`opt(`decorated), `HBox(
        `HSpacing(1),
        `HCenter(`HSquash(`VBox(
            `HCenter(`HSquash(`VBox(
                // This is the heading of the popup box
                `Left(`Heading(_("Confirm Network Restart"))),
                `VSpacing(0.5),
                // This is in information message. Next come the
                // hardware class name (network cards).
                `HVCenter(`Label(_("Because of bridged network YaST2 needs to restart network to apply the settings."))),
                `VSpacing(0.5)
            ))),
            `ButtonBox (
                `HWeight (1, `PushButton (`id(`ok), `opt(`default, `okButton),  Label::OKButton())),
                /* PushButton label */
                `HWeight (1, `PushButton (`id (`cancel), `opt (`cancelButton),  Label::CancelButton()))
            ),
            `VSpacing(0.2)
        ))),
        `HSpacing(1)
    ));

    UI::SetFocus (`id(`ok));

    // for autoinstallation popup has timeout 10 seconds (#192181)
    // timeout for every case (bnc#429562)
    any ret = UI::TimeoutUserInput(10*1000);
	if (ret==`ok){
	 y2internal("Restarting network because of bridged proposal");
	 Service::Restart("network");
	}
     UI::CloseDialog();
    }

    // For ssh/vnc installation don't reload/restart network because possibility of IP change (bnc#347482)
    else if (Stage::cont() && (Linuxrc::usessh() || Linuxrc::vnc() || Linuxrc::display_ip())) 
	y2milestone("For ssh or vnc installation don't reload/restart network during installation.");
    else if (LanItems::force_restart)
	Service::Restart("network");
    else if(Service::Status("network")==0){
	Service::Reload("network");
    }
    else Service::Restart("network");
    sleep(sl);
    }

    if(Abort()) return false;
    /* Progress step 10 */
    ProgressNextStage(_("Running SuSEconfig..."));
    if(!write_only) RunSuSEconfig();
    sleep(sl);

    if (!NetworkService::IsManaged() /*&& (boolean) SCR::Read(.init.scripts.exists, "smpppd")*/ && !write_only )
     {
       if(Abort()) return false;
       /* Progress step 11 */
       ProgressNextStage(_("Setting up smpppd(8)..."));
       // takes care of autoinst by itself
       SetupSMPPPD(false);
       sleep(sl);

     }

    if (NetworkService::IsManaged())
     {
      boolean network=false;
      integer timeout=15;
      while(timeout>0)
      {
	if (NetworkService::isNetworkRunning()){
	 network=true;
	 break;
	}
	y2milestone("waiting for network ... %1", timeout);
	sleep(1000);
	timeout = timeout-1;
      }
     if (!network) {
		if (automatic_configuration) y2error("No network running");
			else Popup::Error(_("No network running"));
		}
    }

    /* Final progress step */
    ProgressNextStage(_("Finished"));
    sleep(sl);

    Progress::Finish();

    if(Abort()) return false;
    return true;
}

/**
 * Only write configuration without starting any init scripts and SuSEconfig
 * @return true on success
 */
global define boolean WriteOnly() {
    write_only = !LanItems::autoinstall_settings["start_immediately"]:false;
    return Write();
}

/**
 * Import data
 * @param settings settings to be imported
 * @return true on success
 */
global define boolean Import(map settings) {

        NetworkInterfaces::Import("netcard", (map<string, map>) settings["devices"]:$[]);
	foreach(string device, NetworkInterfaces::List("netcard"), {
	LanItems::AddNew();
	LanItems::Items[LanItems::current]=$["ifcfg":device];
	});

    LanItems::autoinstall_settings["start_immediately"] = settings["start_immediately"]:false;
    LanItems::autoinstall_settings["strict_IP_check_timeout"] = settings["strict_IP_check_timeout"]:-1;

    NetworkConfig::Import(settings["config"]:$[]);
    DNS::Import((map) eval(settings["dns"]:$[]));
    Routing::Import((map) eval(settings["routing"]:$[]));
    NetworkService::SetManaged (settings["managed"]:false);

    LanItems::modified = true;
    return true;
}

/**
 * Export data
 * @return dumped settings (later acceptable by Import())
 */
global define map Export() {
 map devices = NetworkInterfaces::Export("netcard");
 map udev_rules = LanUdevAuto::Export(devices);
    return $[
	"dns"		: DNS::Export(),
	// FIXME: MOD "modules"	: Modules,
	"s390-devices"  : udev_rules["s390-devices"]:$[],
	"net-udev"  	: udev_rules["net-udev"]:$[],
	"config"	: NetworkConfig::Export(),
	"devices"	: devices,
//	"hwcfg"		: NetworkModules::Export("netcard"),
	"routing"	: Routing::Export(),
	"managed"	: NetworkService::IsManaged (),
	"start_immediately": LanItems::autoinstall_settings["start_immediately"]:false, //start_immediately,
    ];
}

/**
 * Create a textual summary and a list of unconfigured devices
 * @param mode "split": split configured and unconfigured?<br />
 *             "summary": add resolver and routing symmary,
 *		"proposal": for proposal, add links for direct config
 * @return summary of the current configuration
 */
global define list Summary(string mode) {

    boolean split = (mode == "split");

//    list sum = BuildSummary("netcard", LanItems::Hardware, split, mode == "proposal");
    list sum = LanItems::BuildLanOverview();

    /* Testing improved summary */
    if(mode == "summary")
	sum[0] = sum[0]:"" + DNS::Summary() + Routing::Summary();

    return sum;
}

/**
 * Create a textual summary for the general network settings 
 * proposal (NetworkManager + ipv6)
 * @return [rich text, links]
 */
global define list SummaryGeneral () {
    string status_nm = nil;
    string status_v6 = nil;
    string status_virt_net = nil;
    string href_nm = nil;
    string href_v6 = nil;
    string href_virt_net = nil;
    string link_nm = nil;
    string link_v6 = nil;
    string link_virt_net = nil;
    string header_nm = _("Network Mode");

    if (NetworkService::IsManaged ())
    {
	href_nm = "lan--nm-disable";
	// network mode: the interfaces are controlled by the user
	status_nm = _("Interfaces controlled by NetworkManager");
	// disable NetworkManager applet
	link_nm = Hyperlink (href_nm, _("Disable NetworkManager"));
    }
    else
    {
	href_nm = "lan--nm-enable";
	// network mode
	status_nm = _("Traditional network setup with NetControl - ifup");
	// enable NetworkManager applet
	// for virtual network proposal (bridged) don't show hyperlink to enable networkmanager
	link_nm = (virt_net_proposal) ? "..." : Hyperlink (href_nm, _("Enable NetworkManager"));
    }

    if (ipv6)
    {
	href_v6 = "ipv6-disable";
	// ipv6 support is enabled
	status_v6 = _("Support for IPv6 protocol is enabled");
	// disable ipv6 support
	link_v6 = Hyperlink (href_v6, _("Disable IPv6"));
    }
    else
    {
	href_v6 = "ipv6-enable";
	// ipv6 support is disabled
	status_v6 = _("Support for IPv6 protocol is disabled");
	// enable ipv6 support
	link_v6 = Hyperlink (href_v6, _("Enable IPv6"));
    }
   if ( (PackageSystem::Installed("xen") && !Arch::is_xenU()) || PackageSystem::Installed("kvm")||
         PackageSystem::Installed("qemu")||PackageSystem::Installed("virtualbox-ose")
    ){
    if (virt_net_proposal){
	href_virt_net = "virtual-revert";
	status_virt_net = _("Proposed bridged configuration for virtual machine network");
	link_virt_net = Hyperlink(href_virt_net, _("Use non-bridged configuration"));
    } else {
        href_virt_net = "virtual-enable";
        status_virt_net = _("Proposed non-bridged network configuration");
        link_virt_net = Hyperlink(href_virt_net, _("Use bridged configuration"));
    }
   }
    string descr = sformat ("<ul><li>%1: %2 (%3)</li></ul> 
			     <ul><li>%4 (%5)</li></ul>", header_nm, status_nm, link_nm,
			     status_v6, link_v6);
    if (link_virt_net!=nil) descr=sformat("%1
						<ul><li>%2 (%3)</li></ul>", descr, status_virt_net, link_virt_net);
    list<string> links = [ href_nm, href_v6 ];
    if (href_virt_net!=nil) links = add (links, href_virt_net);
    return [descr, links];
}




/**
 * Add a new device
 * @return true if success
 */
global define boolean Add() {
    if(LanItems::Select("") != true) return false;
    NetworkInterfaces::Add();
    return true;
}

/**
 * Edit the given device
 * @param name device to edit
 * @return true if success
 */
global define boolean Edit(string name) {
    LanItems::operation = nil;
    if(LanItems::Select(name) != true) return false;
    NetworkInterfaces::Edit(name);
    LanItems::operation = `edit;
    LanItems::interfacename = NetworkInterfaces::Name;
    return true;
}
/**
 * Delete the given device
 * @param name device to delete
 * @return true if success
 */
global define boolean Delete() {
 LanItems::DeleteItem();
 return true;
}


/**
 * Uses product info and is subject to installed packages.
 * @return Should NM be enabled?
 */ 
define boolean UseNetworkManager () {
    boolean nm_default = false;
    string nm_feature = ProductFeatures::GetStringFeature ("network", "network_manager");
    if (nm_feature == "")
    {
	// compatibility: use the boolean feature
	// (defaults to false)
	nm_default = ProductFeatures::GetBooleanFeature ("network", "network_manager_is_default");
    }
    else if (nm_feature == "always")
    {
	nm_default = true;
    }
    else if (nm_feature == "laptop")
    {
	nm_default = Arch::is_laptop ();
	y2milestone ("Is a laptop: %1", nm_default);
    }
    else // nm_feature == "never"
    {
	nm_default = false;
    }

    boolean nm_installed = Package::Installed ("NetworkManager");
    y2milestone ("NetworkManager wanted: %1, installed: %2", nm_default, nm_installed);
    return nm_default && nm_installed;
}

/**
 * Create minimal ifcfgs for the case when NetworkManager is used:
 * NM does not need them but yast2-firewall and SuSEfirewall2 do
 * Avoid existing ifcfg from network installation
 */
define void ProposeNMInterfaces () {
    y2milestone ("Minimal ifcfgs for NM");
    foreach (integer number, any lanitem, LanItems::Items, {
	if (hasAnyValue(((map)lanitem)["hwinfo", "dev_name"]:"")) {
	LanItems::current = number;
	if (!LanItems::IsItemConfigured()){
	  y2milestone("Nothing already configured start proposing %1 (NM)", LanItems::getCurrentItem());
	  LanItems::ProposeItem();
	 }
	}
    });

}

/**
 * Propose interface configuration
 * @return true if something was proposed
 */
global define boolean ProposeInterfaces () {

    y2milestone("Hardware=%1", LanItems::Hardware);
//    y2milestone("InstallInf=%1", InstallInf);

    y2milestone("NetworkConfig::Config=%1", NetworkConfig::Config);
    y2milestone("NetworkConfig::DHCP=%1", NetworkConfig::DHCP);

 // test if we have any virtualization installed
 if (virt_net_proposal)
 {
  y2milestone("Virtualization [xen|kvm|qemu|virtualbox-ose] detected - will propose virtualization network");
    // in case of virtualization use special proposal
    // collect all interfaces that will be skipped from bridged proposal
    list<string> skipped = [];
    foreach(integer current, any config, LanItems::Items, {
     string ifcfg = LanItems::Items[current, "ifcfg"]:"";
     if (NetworkInterfaces::GetType(ifcfg)=="br"){
       NetworkInterfaces::Edit(ifcfg);
       y2milestone("Bridge %1 with ports (%2) found", ifcfg, NetworkInterfaces::Current["BRIDGE_PORTS"]:"");
	skipped=add(skipped, ifcfg);
        foreach(string port, (list<string>) splitstring(NetworkInterfaces::Current["BRIDGE_PORTS"]:"", " "), {
	  skipped=add(skipped, port);
	});
     }
     if (NetworkInterfaces::GetType(ifcfg)=="bond"){
      NetworkInterfaces::Edit(ifcfg);
      foreach(string i, (list<string>)splitstring("0,1,2,3,4,5,6,7,8,9", ","), {
	string slave = NetworkInterfaces::Current[sformat("BONDING_SLAVE%1", i)]:"";
	y2milestone("For interface %1 found slave %2", ifcfg, slave);
	if(size(slave)>0) skipped=add(skipped, slave);
      });
     }
    if (NetworkInterfaces::GetValue(ifcfg, "STARTMODE")=="nfsroot"){
     y2milestone("Skipped %1 interface from bridge slaves because of nfsroot.", ifcfg);
     skipped=add(skipped, ifcfg);
    }
    });
    y2milestone("Skipped interfaces : %1", skipped);

    // configure all connected unconfigured devices with dhcp (with default parameters)
    foreach (integer number, any lanitem, LanItems::Items, {
	if (hasAnyValue(((map)lanitem)["hwinfo", "dev_name"]:"")) {
	LanItems::current = number;
        boolean valid = LanItems::getCurrentItem()["hwinfo", "link"]:false == true;
	if (!valid) y2warning("item number %1 has link:false detected", number);
	   // don't propose WLAN interface (bnc#450670)
	    else if (LanItems::getCurrentItem()["hwinfo", "type"]:""=="wlan"){
		 y2warning("not proposing WLAN interface");
		 valid = false;
		}
	if (!LanItems::IsItemConfigured() && valid && !contains(skipped, LanItems::getCurrentItem()["hwinfo", "dev_name"]:"")){
	  y2milestone("Not configured - start proposing");
	  LanItems::ProposeItem();
	 }
	}
    });

    // then each configuration (except bridges) move to the bridge
    // and add old device name into bridge_ports
    foreach(integer current, any config, LanItems::Items, {
     string ifcfg = LanItems::Items[current, "ifcfg"]:"";
     if (contains(skipped, ifcfg)){
	y2milestone("Skipping interface %1", ifcfg);
	continue;
     }
      if (size(ifcfg)>0){
       NetworkInterfaces::Edit(ifcfg);
       map<string, any> old_config = NetworkInterfaces::Current;
       y2debug("Old Config %1\n%2", ifcfg, old_config);
       string new_ifcfg = sformat("br%1", NetworkInterfaces::GetFreeDevice("br"));
       y2milestone("old configuration %1, bridge %2", ifcfg, new_ifcfg);
       NetworkInterfaces::Name = new_ifcfg;
       // from bridge interface remove all bonding-related stuff
       foreach(string key, any value, NetworkInterfaces::Current, {
	if (issubstring(key, "BONDING")){
	 NetworkInterfaces::Current[key]=nil;
	}
       });
       NetworkInterfaces::Current["BRIDGE"]="yes";
       NetworkInterfaces::Current["BRIDGE_PORTS"]=ifcfg;
       NetworkInterfaces::Current["BRIDGE_STP"]="off";
       NetworkInterfaces::Current["BRIDGE_FORWARDDELAY"]="0";
	// hardcode startmode (bnc#450670), it can't be ifplugd!
       NetworkInterfaces::Current["STARTMODE"]="auto";
       // remove description - will be replaced by new (real) one
       NetworkInterfaces::Current=remove(NetworkInterfaces::Current, "NAME");
       if( NetworkInterfaces::Commit() ){
	NetworkInterfaces::Add();
	NetworkInterfaces::Edit(ifcfg);
	old_config["BOOTPROTO"]="static";
	old_config["IPADDR"]="0.0.0.0/32";
	// remove all aliases (bnc#590167)
	foreach (string a, any v, (map <string, any>)NetworkInterfaces::Current["_aliases"]:$[], {
	    if (v != nil) { NetworkInterfaces::DeleteAlias (NetworkInterfaces::Name, a); }
	});
	old_config["_aliases"]=$[];
        y2milestone("Old Config with apllied changes %1\n%2", ifcfg, old_config);
	NetworkInterfaces::Current = old_config;
        NetworkInterfaces::Commit();

        LanItems::Items[current, "ifcfg"] = new_ifcfg;
        LanItems::modified=true;
        LanItems::force_restart=true;
        y2internal("List %1", NetworkInterfaces::List(""));
        // re-read configuration to see new items in UI
        LanItems::Read();
      }
     } else y2warning("empty ifcfg");
    });
 } else {

    if (! LanItems::nm_proposal_valid)
    {
	NetworkService::SetManaged (UseNetworkManager ());
	LanItems::nm_proposal_valid = true;
    }

    if (NetworkService::IsManaged ())
    {
	ProposeNMInterfaces ();

	LanItems::modified = true;	// #144139 workaround
	y2milestone ("NM proposal");
	return true;
    }


  }
    /* Something is already configured -> do nothing */
    boolean configured=false;
    foreach (integer number, any lanitem, LanItems::Items, {
	LanItems::current = number;
	if (LanItems::IsItemConfigured()){
	 y2milestone("Something already configured: don't propose.");
	 configured = true;
	 break;
        }
      });
    if (configured) return false;


    foreach (integer number, any lanitem, LanItems::Items, {
	if (hasAnyValue(((map)lanitem)["hwinfo", "dev_name"]:"")) {
	LanItems::current = number;
        boolean link = LanItems::getCurrentItem()["hwinfo", "link"]:false;
	if (LanItems::getCurrentItem()["hwinfo", "type"]:"" == "wlan")
		y2warning("Will not propose wlan interfaces");
	else{
		if (!link) y2warning("item number %1 has link:false detected", number);
			else if (!LanItems::IsItemConfigured() && link){
			  y2milestone("Nothing already configured - start proposing");
			  LanItems::ProposeItem();
			  break;
			 }
	    }
	}
    });




    y2milestone("NetworkConfig::Config=%1", NetworkConfig::Config);
    y2milestone("NetworkConfig::DHCP=%1", NetworkConfig::DHCP);

    return true;
}

/**
 * Propose the hostname
 * See also DNS::Read
 * @return true if something was proposed
 */
define boolean ProposeHostname () {
    if (DNS::proposal_valid)
    {
	// the standalone hostname dialog did the job already
	return false;
    }

    return true;
}

/**
 * Propose configuration for routing and resolver
 * @return true if something was proposed
 */
global define boolean ProposeRoutesAndResolver () {
    if(LanItems::bootproto == "static" && LanItems::ipaddr != "" && LanItems::ipaddr != nil) {
	ProposeHostname ();
    }
    return true;
}

/**
 * Propose a configuration
 * @return true if something was proposed
 */
global define boolean Propose() {
	NetworkInterfaces::CleanCacheRead();
	LanItems::Read();
    return
	ProposeInterfaces () &&
	ProposeRoutesAndResolver ();
}

/**
 * Create a configuration for autoyast
 * @return true if something was proposed
 */
global define boolean Autoinstall() {

    y2milestone("Hardware=%1", LanItems::Hardware);
    map tosel = nil;

    /* Some HW found -> use it for proposal */
    if(size(LanItems::Hardware) > 0
       && size(LanItems::autoinstall_settings["interfaces"]:[]) > 0) {

        foreach(map<string, string> interface, LanItems::autoinstall_settings["interfaces"]:[], ``{
                
            list<string> devs = NetworkInterfaces::List("netcard");
            y2milestone("devs: %1", devs);
            tosel = nil;

            Add();

    	    tosel = LanItems::FindMatchingDevice(interface);

	    y2milestone("tosel=%1", tosel);

            // Read module data from autoyast
            map aymodule = LanItems::GetModuleForInterface(interface["device"]:"",
                LanItems::autoinstall_settings["modules"]:[]);


	    if (tosel != nil)
	    {
		tosel["module"] = (aymodule["module"]:""!= "")?
		    aymodule["module"]:"" : tosel["module"]:"";
		tosel["options"] = (aymodule["options"]:""!= "")?
		    aymodule["options"]:"" : tosel["options"]:"";

		LanItems::SelectHWMap(tosel);
	    }
	    else {
		y2milestone("No hardware, no install.inf -> no autoinstallation possible.");
		return false;
	    }

	    // The uppercasing is also done in lan_auto::FromAY
	    // but the output goes to "devices" whereas here
	    // we use "interfaces". FIXME.
            string newk = nil;
            interface =  mapmap(string k, string v, interface, ``{
                    newk = toupper(k);
                    return $[newk: v];
                    });


	    map defaults = union (LanItems::SysconfigDefaults, LanItems::GetDefaultsForHW ());

            // Set interface variables
	    LanItems::SetDeviceVars(interface, defaults);

	    y2debug("ipaddr,bootproto=%1,%2", LanItems::ipaddr, LanItems::bootproto);
            if(LanItems::bootproto == "static" && LanItems::ipaddr != "" && LanItems::ipaddr != nil)
            {

                y2milestone("static configuration");

		if(LanItems::netmask == nil || LanItems::netmask == "")
		    LanItems::netmask = "255.255.255.0";

	    }
	    LanItems::Commit();
	});
    }
    else 
    {
        y2milestone ("no interface configuration, taking it from install.inf");
        ProposeInterfaces ();
    }

    // #153426 - using ProposeInterfaces instead of Propose omitted these
    // if they are nonempty, Import has already taken care of them.
    if (LanItems::autoinstall_settings["routing", "routes"]:[] == [])
    {
	y2milestone ("gateway from install.inf");
//	Routing::ReadFromGateway (InstallInf["gateway"]:"");
    }
    if (LanItems::autoinstall_settings["dns", "nameservers"]:[] == [])
    {
	y2milestone ("nameserver from install.inf");
//	DNS::ReadNameserver (InstallInf["nameserver"]:"");
    }
    if (LanItems::autoinstall_settings["dns", "hostname"]:"" == "")
    {
	ProposeHostname ();
    }

    return true;
}


/**
 * Check if any device  is configured with DHCP.
 * @return true if any DHCP device is configured
 */
global define boolean AnyDHCPDevice() {
 // return true if there is at least one device with dhcp4, dhcp6, dhcp or dhcp+autoip
   return size(
	  union(
	    union(
	     NetworkInterfaces::Locate("BOOTPROTO", "dhcp4"),
	     NetworkInterfaces::Locate("BOOTPROTO", "dhcp6")
		 ),
	   union(
	     NetworkInterfaces::Locate("BOOTPROTO", "dhcp"),
	     NetworkInterfaces::Locate("BOOTPROTO", "dhcp+autoip")
		)
	       )
	      ) > 0;
}


global define list<map> PrepareForAutoinst() {
  
//    ReadInstallInf();     
    LanItems::ReadHw();
    return LanItems::Hardware;
}

/**
 * @return list of packages needed when writing the config
 */
global list<string> Packages () {
    list<string> pkgs = [];
   map<string, any> required = $[
			"types":$[
			//for wlan require iw instead of wireless-tools (bnc#539669)
				"wlan" : "iw",
				"vlan" : "vlan",
				"br"   : "bridge-utils",
				"tun"  : "tunctl",
				"tap"  : "tunctl"
			],
			"options":$[
				"STARTMODE":$[
					"ifplugd" : "ifplugd"
				],
				"WIRELESS_AUTH_MODE":$[
					"psk" : "wpa_supplicant",
					"eap" : "wpa_supplicant"
				]
			]
		];

    foreach(string type, (list<string>)Map::Keys(required["types"]:$[]), {
     string package=required["types", type]:"";
     if (size( NetworkInterfaces::List(type) ) > 0) {
	y2milestone("Network interface type %1 requires package %2", type, package);
	if (!PackageSystem::Installed( package )) pkgs = add (pkgs, package);
    }
    });


    foreach(string type, (list<string>)Map::Keys(required["options"]:$[]), {
     foreach(string option, (list<string>)Map::Keys(required["options", type]:$[]), {
      string package=required["options", type, option]:"";
      if (NetworkInterfaces::Locate (type, option) != []){
       y2milestone("Network interface with options %1, %2 requires package %3", type, option, package);
       if (!PackageSystem::Installed(package)) pkgs = add (pkgs, package);
      }
     });
    });

    if (NetworkService::IsManaged ())
    {
	if (!PackageSystem::Installed("NetworkManager")) pkgs = add (pkgs, "NetworkManager");
    }
    return pkgs;
}

/**
 * @return list of packages needed when writing the config in autoinst
 * mode
 */
global map AutoPackages () {
      return ($["install": Packages(), "remove": []]);
}

/**
 * Xen bridging confuses us (#178848)
 * @return whether xenbr* exists
 */
global define boolean HaveXenBridge () {
    //adapted test for xen bridged network (bnc#553794)
    boolean have_br = FileUtils::Exists("/dev/.sysconfig/network/xenbridges");
    y2milestone ("Have Xen bridge: %1", have_br);
    return have_br;
}

/* EOF */
}

ACC SHELL 2018