ACC SHELL
/**
* File:
* Nfs.ycp
*
* Module:
* Configuration of nfs
*
* Summary:
* NFS client configuration data, I/O functions.
*
* Authors:
* Jan Holesovsky <kendy@suse.cz>
* Dan Vesely <dan@suse.cz>
* Martin Vidner <mvidner@suse.cz>
*
* $Id: Nfs.ycp 60683 2010-02-03 18:19:18Z kmachalkova $
*/
{
module "Nfs";
textdomain "nfs";
import "Mode";;
import "Report";
import "Service";
import "Summary";
import "SuSEFirewall";
import "Progress";
import "PackageSystem";
import "Wizard";
include "nfs/routines.ycp";
/* default value of settings modified */
global boolean modified = false;
/* Should fstab reading be skipped ? (yes if we're
embedded in partitioner) */
global boolean skip_fstab = false;
/**
* Function sets internal variable, which indicates, that any
* settings were modified, to "true"
*/
global define void SetModified () {
modified = true;
}
/**
* Functions which returns if the settings were modified
* @return boolean settings were modified
*/
global define boolean GetModified () {
return modified;
}
/**
* Required packages
*/
global list <string> required_packages = [ "nfs-client" ];
/**
* eg.: [ $["spec": "moon:/cheese", file: "/mooncheese", "mntopts": "defaults"], ...]
*/
global list<map<string,any> > nfs_entries = [];
/**
* Read only, intended for checking mount-point uniqueness.
*/
global list<map> non_nfs_entries = [];
global boolean nfs4_enabled = false;
global string idmapd_domain = "";
string portmapper = "";
// list of created directories
list<string> created_dirs = [];
boolean ReadNfs4 () {
return (SCR::Read(.sysconfig.nfs.NFS4_SUPPORT)=="yes");
}
string ReadIdmapd() {
return (string) SCR::Read(.etc.idmapd_conf, "Domain");
}
/**
* Set module data
* @param settings module settings
* @return void
*/
global define void Set(map <string, any> settings) ``{
if ( haskey(settings, "enable_nfs4") ) {
nfs4_enabled = settings["enable_nfs4"]:false;
}
else
nfs4_enabled = ReadNfs4();
if ( haskey(settings, "idmapd_domain") ) {
idmapd_domain = settings["idmapd_domain"]:"localdomain";
}
else
idmapd_domain = ReadIdmapd();
nfs_entries = maplist(map entry, settings["nfs_entries"]:[], ``{
return($[
"spec":entry["server_path"]:"",
"file":entry["mount_point"]:"",
"vfstype":entry["vfstype"]:"",
"mntops":entry["nfs_options"]:""
]);
});
return;
}
/**
* Get all NFS configuration from a map.
* When called by nfs_auto (preparing autoinstallation data)
* the map may be empty.
* @param settings a map with a single key: nfs_entries
* @return success
*/
global define boolean Import (map <string, any> settings) ``{
boolean missing = false;
settings["nfs_entries"] = maplist(map s,settings["nfs_entries"]:[],``{
foreach (string k, ["server_path", "mount_point", "nfs_options"], ``{
if (! haskey (s, k))
{
y2error ("Missing at Import: '%1'.", k);
missing = true;
}
});
//Backwards compatibility: with FaTE#302031, we support nfsv4 mounts
//thus we need to keep info on nfs version (v3 vs. v4)
//But older AY profiles might not contain this element
//so let's assume nfsv3 in that case (#395850)
if ( !haskey(s, "vfstype") )
{
s["vfstype"] = "nfs";
}
return s;
});
if (missing)
{
return false;
}
Set(settings);
return true;
}
/**
* Dump the NFS settings to a map, for autoinstallation use.
* @return a list of nfs entries.
*/
global define map Export () ``{
map <string, any> settings = $[];
settings["enable_nfs4"] = nfs4_enabled;
settings["idmapd_domain"] = idmapd_domain;
list entries = maplist(map entry, nfs_entries, ``{
return($[
"server_path":entry["spec"]:"",
"mount_point":entry["file"]:"",
"vfstype" :entry["vfstype"]:"",
"nfs_options":entry["mntops"]:""
]);
});
settings["nfs_entries"]= entries;
return settings;
}
/* ------------------------------------------------------------ */
// Space escaping.
// This should be done by the agent, but any-agent sucks.
/**
* Escape spaces " " -> "\\040".
* @param s a string or nil
* @return escaped string or nil
*/
define string EscapeSpaces1 (string s) ``{
return s == nil ? nil : mergestring (splitstring (s, " "), "\\040");
}
/**
* Escape spaces " " -> "\\040" in all values of all entries
* @param entries a list of maps, such as nfs_entries
* @return escaped entries
*/
define list<map<string,any> > EscapeSpaces (list<map<string,any> > entries) ``{
return maplist (map<string,any> entry, entries,
``(mapmap (string key, any value, entry, ``(
$[ key :
(is (value, string)?
EscapeSpaces1 ((string) value): value)]
))
));
}
/**
* (like awk gsub, but return the result, not number of replacements)
* replaces from back!
* @param regex regular expression to replace
* @param replacement the replacement string
* @param s where to replace
* @return the changed string
*/
define string gsub (string regex, string replacement, string s) ``{
string temp = nil;
while (true)
{
// argh, regexpsub logs an error if it cannot sub
if (! regexpmatch (s, ".*" + regex + ".*"))
{
break;
}
temp = regexpsub (s, "(.*)"+regex+"(.*)", "\\1"+replacement+"\\2");
if (temp == nil)
{
break;
}
s = temp;
}
return s;
}
/**
* Un-escape spaces "\\040" -> " "
* @param s string or nil
* @return escaped string or nil
*/
define string UnescapeSpaces1 (string s) ``{
// escaped space, \040, is /\\040/
// which is "\\\\040"
return s == nil ? nil : gsub ("\\\\040", " ", s);
}
/**
* Un-escape spaces "\\040" -> " " in all values of all entries
* @param entries a list of maps, such as nfs_entries
* @return escaped entries
*/
define list<map<string,any> > UnescapeSpaces (list<map<string,any> > entries) ``{
return maplist (map<string,any> entry, entries,
``( mapmap (string key, any value, entry, ``(
$[ key:
(is (value, string)?
UnescapeSpaces1 ((string) value): value)]
))
));
}
global string FindPortmapper() {
//testsuite is dumb - it can't distinguish between the existence
//of two services - either both exists or both do not
if (Mode::testsuite())
return "portmap";
return Service::Find( ["rpcbind", "portmap" ] );
}
/* ------------------------------------------------------------ */
/**
* Reads NFS settings from the SCR (.etc.fstab)
* @return true on success
*/
global define boolean Read () ``{
//Read /etc/fstab if we're running standalone (otherwise, libstorage does the job)
if( !skip_fstab )
{
list<map<string,any> > fstab = (list<map<string,any> >) SCR::Read (.etc.fstab);
fstab = UnescapeSpaces (fstab);
y2milestone ("fstab: %1", fstab);
// For simplicity, this leaves also the unused fileds in the maps.
nfs_entries = filter (map<string,any> entry, fstab, ``{
return (entry["vfstype"]:"" == "nfs" || entry["vfstype"]:"" == "nfs4");
});
non_nfs_entries = filter (map entry, fstab, ``{
return (entry["vfstype"]:"" != "nfs" && entry["vfstype"]:"" != "nfs4");
});
}
nfs4_enabled = (SCR::Read(.sysconfig.nfs.NFS4_SUPPORT)=="yes");
idmapd_domain = (string) SCR::Read(.etc.idmapd_conf, "Domain");
boolean progress_orig = Progress::set(false);
SuSEFirewall::Read ();
Progress::set(progress_orig);
portmapper = FindPortmapper();
//There is neither rpcbind nor portmap
if ( portmapper == "" )
{
//so let's install rpcbind (default since #423026)
required_packages = add( required_packages, "rpcbind" );
portmapper = "rpcbind";
}
if (nfs4_enabled)
required_packages = add( required_packages, "nfsidmap");
if ( !PackageSystem::CheckAndInstallPackagesInteractive( required_packages ) )
return false;
return true;
}
/**
* Writes the NFS client configuration without
* starting/stopping the service.
* Autoinstallation uses this and then calls SuSEconfig only once
* and starts the services together.
* (No parameters because it is too short to abort)
* @return true on success
*/
global define boolean WriteOnly () ``{
// Merge with non-nfs entries from fstab:
list<map<string,any> > fstab = (list<map<string,any> >) SCR::Read (.etc.fstab);
// unescape deferred for optimization
fstab = filter (map<string,any> entry, fstab, ``{
return ( entry["vfstype"]:"" != "nfs" && entry["vfstype"]:"" != "nfs4");
});
fstab = UnescapeSpaces (fstab);
foreach (map<string,any> entry, nfs_entries, ``{
fstab = add (fstab, (map<string,any>)union (entry, $[/*"vfstype": "nfs",*/ "freq": 0, "passno": 0]));
// create mount points
string file = entry["file"]:"";
if (!(boolean) SCR::Execute (.target.mkdir, file))
{
// error popup message
Report::Warning (sformat (_("Unable to create directory '%1'."), file));
}
});
y2milestone ("fstab: %1", fstab);
SCR::Execute (.target.bash, "/bin/cp $ORIG $BACKUP", $["ORIG" : "/etc/fstab", "BACKUP" : "/etc/fstab.YaST2.save"]);
fstab = EscapeSpaces (fstab);
if (!SCR::Write(.etc.fstab, fstab))
{
// error popup message
Report::Error (_("Unable to write to /etc/fstab.
No changes will be made to the
the NFS client configuration.\n"));
return false;
}
/* if (size (nfs_entries) == 0)
{
// Service::Adjust ("nfs", "disable"); // what if autofs needs it?
}*/
if (size (nfs_entries) != 0)
{
Service::Enable( portmapper );
Service::Enable("nfs");
// #36737: it just runs sm_notify at boot time
// replaces rpc.statd
Service::Enable("nfsboot");
}
if(nfs4_enabled)
{
SCR::Write(.sysconfig.nfs.NFS4_SUPPORT,"yes");
SCR::Write(.etc.idmapd_conf, ["Domain", idmapd_domain] );
}
else
{
SCR::Write(.sysconfig.nfs.NFS4_SUPPORT,"no");
}
boolean progress_orig = Progress::set (false);
SuSEFirewall::WriteOnly ();
Progress::set (progress_orig);
return true;
}
/**
* Writes the NFS client configuration and starts/stops the service.
* (No parameters because it is too short to abort)
* @return true on success
*/
global define boolean Write () ``{
if (WriteOnly ())
{
// dialog label
Progress::New (_("Writing NFS Configuration"), " ", 2, [
// progress stage label
_("Stop services"),
// progress stage label
_("Start services"),
], [
// progress step label
_("Stopping services..."),
// progress step label
_("Starting services..."),
// final progress step label
_("Finished") ],
"" );
// help text
Wizard::RestoreHelp(_("Writing NFS client settings. Please wait..."));
Progress::NextStage ();
Service::Stop("nfs");
Progress::NextStage ();
if (size (nfs_entries) > 0)
{
if (Service::Status ( portmapper ) != 0)
{
// portmap must not be started if it is running already (see bug # 9999)
Service::Start( portmapper );
}
Service::Start("nfs");
// #74597: if all mounts are noauto, $? is 6 (unconfigured)
integer status = Service::Status("nfs");
if (status != 0 && status != 6)
{
// error popup message
Report::Error (_("Unable to mount the NFS entries from /etc/fstab."));
return false;
}
}
boolean progress_orig = Progress::set (false);
SuSEFirewall::ActivateConfiguration ();
Progress::set (progress_orig);
Progress::NextStage ();
return true;
}
else
{
return false;
}
}
/**
* Summary()
* @return Html formatted configuration summary
*/
global define string Summary () ``{
string summary = "";
string nc = Summary::NotConfigured ();
// summary header
summary = Summary::AddHeader(summary, _("NFS Entries"));
integer entries = size(nfs_entries);
y2milestone("Entries: %1", nfs_entries);
// summary item, %1 is a number
string configured = sformat(_("%1 entries configured"), entries);
summary = Summary::AddLine(summary, (entries>0) ? configured : nc);
return summary;
}
/**
* Mount NFS directory
* @param server remote server name
* @param share name of the exported directory
* @param mpoint mount point (can be empty or nil, in this case it will be mounted in temporary directory)
* @param options mount options - e.g. "ro,hard,intr", see man nfs
* @param type nfs type (nfs vs. nfsv4) - if empty, 'nfs' is used
* @return string directory where volume was mounted or nil if mount failed
*/
global define string Mount(string server, string share, string mpoint, string options, string type) ``{
if (size(server) == 0 || size(share) == 0)
{
return nil;
}
// check if options are valid
if (size(options) > 0)
{
if (check_options(options) != "")
{
y2warning("invalid mount options: %1", options);
return nil;
}
}
// mount to temporary directory if mpoint is nil
if (mpoint == nil)
{
string tmpdir = (string) SCR::Read(.target.tmpdir);
if (tmpdir == nil || tmpdir == "")
{
y2security("Warning: using /tmp directory!");
tmpdir = "/tmp";
}
mpoint = tmpdir + "/nfs" + sformat("%1", size(created_dirs)); // use num to allow parallel mounts
}
// check mount point
if (CheckPath(mpoint) == false)
{
// mount point is not valid
y2warning("invalid mount point: %1", mpoint);
return nil;
}
string portmapper = FindPortmapper();
// check whether portmapper is installed
if (IsPortmapperInstalled( portmapper ) == false)
{
y2warning("Neither rpcbind nor portmap is installed");
return nil;
}
// start portmapper if it isn't running
if (Service::Status( portmapper ) != 0)
{
if (Service::Start( portmapper ) == false)
{
y2warning( sformat("%1 cannot be started", portmapper) );
return nil;
}
}
// create mount point if it doesn't exist
if (SCR::Read(.target.dir, mpoint) == nil)
{
if (!(boolean) SCR::Execute(.target.mkdir, mpoint))
{
y2warning("cannot create mount point %1", mpoint);
return nil;
}
// remember name of created directory
created_dirs = add(created_dirs, mpoint);
}
// build mount command
string command = sformat("/bin/mount %1 %2 %3:%4 %5", (size(options) > 0) ? "-o " + options : "",
"-t " + (size(type) > 0 ? type : "nfs"),
server, share, mpoint);
// execute mount command
return (SCR::Execute(.target.bash, command) == 0) ? mpoint : nil;
}
/**
* Unmount NFS directory from the system
* @param mpoint NFS mount point to unmount
* @return boolean true on success
*/
global define boolean Unmount(string mpoint) ``{
if (size(mpoint) == 0)
{
return false;
}
// unmount directory if it's NFS mountpoint
list<map<string,any> > mounts = (list<map<string,any> >) SCR::Read(.proc.mounts);
boolean found = false;
foreach(map<string, any> m, mounts,
``{
string type = (string) (m["vfstype"]:nil);
string file = (string) (m["file"]:nil);
if ( (type == "nfs" || type == "nfs4") && file == mpoint)
{
found = true;
}
}
);
if (found)
{
string command = sformat("/bin/umount %1", mpoint);
if (SCR::Execute(.target.bash, command) != 0)
{
return false;
}
}
else
{
y2warning("%1 is not NFS mount point", mpoint);
return false;
}
// if the directory was created by Mount call and it is empty then remove it
if (contains(created_dirs, mpoint) && SCR::Read(.target.dir, mpoint) == [])
{
string command = sformat("/bin/rmdir %1", mpoint);
if (SCR::Execute(.target.bash, command) != 0)
{
return false;
}
// remove directory from list
created_dirs = filter(string d, created_dirs, ``(d != mpoint));
}
return true;
}
/**
* Return required packages for auto-installation
* @return map of packages to be installed and to be removed
*/
global define map AutoPackages() ``{
return ($["install": required_packages, "remove": []]);
}
}
ACC SHELL 2018