ACC SHELL
/**
* File: modules/Profile.ycp
* Module: Auto-Installation
* Summary: Profile handling
* Authors: Anas Nashif <nashif@suse.de>
*
* $Id: Profile.ycp 61506 2010-03-26 08:57:51Z ug $
*/
{
module "Profile";
textdomain "autoinst";
import "Stage";
import "Mode";
import "AutoinstConfig";
import "XML";
import "Label";
import "Popup";
import "ProductControl";
import "Directory";
import "FileUtils";
import "PackageSystem";
include "autoinstall/xml.ycp";
// The Complete current Profile
global map<string,any> current = $[];
// defined in Y2ModuleConfig
global map<string, map> ModuleMap = $[];
global string Version = "";
string Description = "";
global boolean changed = false;
global boolean prepare = true;
/**
* Constructor
* @return void
*/
global define void Profile ()
{
//
// setup profile XML parameters for writing
//
profileSetup();
if (Stage::initial ())
{
SCR::Execute(.target.mkdir, AutoinstConfig::profile_dir);
}
return;
}
/**
* Detect Version
* @return string
*/
define string DetectVersion() {
return "";
}
/**
* compatibility to new storage lib in 10.0
*/
define void storageLibCompat() {
list<map> newPart = [];
foreach( map d, current["partitioning"]:[], {
if( haskey(d, "is_lvm_vg") && d["is_lvm_vg"]:false == true ) {
d = remove( d, "is_lvm_vg" );
d["type"] = `CT_LVM;
} else if( haskey(d, "device") && d["device"]:"" == "/dev/md" ) {
d["type"] = `CT_MD;
} else if( ! haskey(d,"type") ) {
d["type"] = `CT_DISK;
}
// actually, this is not a compatibility hook for the new
// storage lib. It's a hook to be compatibel with the autoyast
// documentation for reusing partitions
//
d["partitions"] = maplist( map p, d["partitions"]:[], ``{
if( haskey(p, "create") && p["create"]:true == false &&
haskey(p, "partition_nr") ) {
p["usepart"] = p["partition_nr"]:0; // useless default
}
if( haskey(p, "partition_id") ) {
// that's a bit strange. There is a naming mixup between
// autoyast and the storage part of yast. Actually filesystem_id
// does not make sense at all but in autoyast it is the
// partition id (maybe that's because yast calls
// the partition id "fsid" internally).
// partition_id in the profile does not work at all, so we copy
// that value to filesystem_id
p["filesystem_id"] = p["partition_id"]:0;
}
return p;
});
newPart = add( newPart, d );
});
y2milestone("partitioning is now %1",newPart);
current["partitioning"] = newPart;
}
define void softwareCompat() {
current["software"] = current["software"]:$[];
if( !contains(current["software","packages"]:[], "autoyast2-installation") )
current["software","packages"] = add( current["software","packages"]:[], "autoyast2-installation" );
/* without autoyast2, <files ...> does not work */
if( haskey( current, "files" ) && !contains(current["software","packages"]:[], "autoyast2") )
current["software","packages"] = add( current["software","packages"]:[], "autoyast2" );
/* workaround for missing "REQUIRES" in content file to stay backward compatible */
/* FIXME: needs a more sophisticated or compatibility breaking solution after SLES11 */
if( size( current["software","patterns"]:[] ) == 0 )
current["software","patterns"] = [ "base" ];
}
/**
* compatibility to new language,keyboard and timezone client in 10.1
*/
global define void generalCompat() {
if( haskey( current, "general" ) ) {
if( haskey( current["general"]:$[], "keyboard" ) ) {
current["keyboard"] = current["general","keyboard"]:$[];
current["general"] = remove( current["general"]:$[], "keyboard" );
}
if( haskey( current["general"]:$[], "language" ) ) {
current["language"] = $[ "language":current["general","language"]:"" ];
current["general"] = remove( current["general"]:$[], "language" );
}
if( haskey( current["general"]:$[], "clock" ) ) {
current["timezone"] = current["general","clock"]:$[];
current["general"] = remove( current["general"]:$[], "clock" );
}
if( current["general","mode","final_halt"]:false ) {
map script = $[ "filename":"zzz_halt", "source":"chkconfig autoyast off\nshutdown -h now" ];
if( ! haskey( current, "scripts" ) )
current["scripts"] = $[];
if( ! haskey( current["scripts"]:$[], "init-scripts" ) )
current["scripts","init-scripts"] = [];
current["scripts","init-scripts"] = add( current["scripts","init-scripts"]:[], script );
}
if( current["general","mode","final_reboot"]:false ) {
map script = $[ "filename":"zzz_reboot", "source":"chkconfig autoyast off\nshutdown -r now" ];
if( ! haskey( current, "scripts" ) )
current["scripts"] = $[];
if( ! haskey( current["scripts"]:$[], "init-scripts" ) )
current["scripts","init-scripts"] = [];
current["scripts","init-scripts"] = add( current["scripts","init-scripts"]:[], script );
}
if( haskey( current["software"]:$[], "additional_locales" ) ) {
if( ! haskey(current, "language") ) {
current["language"] = $[];
}
current["language","languages"] = mergestring( current["software","additional_locales"]:[], "," );
current["software"] = remove( current["software"]:$[], "additional_locales" );
}
}
}
/**
* Read Profile properties and Version
* @param map Profile Properties
* @return void
*/
global define void ReadProperties (map properties) {
Version = properties["version"]:"";
Description = properties["description"]:"";
if (Version != "3.0" || Version == "")
{
Version = DetectVersion();
if (Version == "")
{
y2milestone("Can't detect Profile Version");
return;
}
} else {
y2milestone("AutoYaST Profile Version %1 Detected.", Version);
}
return;
}
/**
* Import Profile
* @param map profile
* @return void
*/
global define void Import(map<string, any> profile)
{
y2milestone("importing profile");
current = profile;
ReadProperties(current["properties"]:$[]);
// old style
if (haskey(profile, "configure") || haskey(profile, "install")) {
map _configure = profile["configure"]:$[];
map _install = profile["install"]:$[];
if( haskey(profile, "configure") )
current = remove( current, "configure" );
if( haskey(profile, "install") )
current = remove( current, "install" );
map tmp = (map<string, any>)union( _configure, _install );
current = (map<string, any>)union( tmp, current );
}
// should not be needed anymore with new libsax
//if (!current["x11", "configure_x11"]:false)
// ProductControl::DisableModule ("x11");
// raise the network immediately after configuring it
if( haskey(current, "networking") && ! haskey(current["networking"]:$[], "start_immediately") ) {
current["networking","start_immediately"] = true;
y2milestone("start_immediately set to true");
}
storageLibCompat(); // compatibility to new storage library (SL 10.0)
generalCompat(); // compatibility to new language,keyboard and timezone (SL10.1)
softwareCompat();
y2debug("Current Profile=%1", current );
return;
}
/**
* Prepare Profile for saving and remove empty data structs
* @return void
*/
global define void Prepare()
{
if (!prepare)
return;
Popup::ShowFeedback(_("Collecting configuration data..."),
_("This may take a while"));
list<string> e = [];
foreach(string p, map d, ModuleMap, {
//
// Set resource name, if not using default value
//
string resource = d["X-SuSE-YaST-AutoInstResource"]:"";
if (resource == "")
resource = p;
string tomerge = d["X-SuSE-YaST-AutoInstMerge"]:"";
string module_auto =d["X-SuSE-YaST-AutoInstClient"]:"none";
if ( (boolean) WFM::CallFunction(module_auto, ["GetModified"]))
{
any resource_data = WFM::CallFunction(module_auto, ["Export"]);
integer s = 0;
if ( tomerge == "") {
if ( d["X-SuSE-YaST-AutoInstDataType"]:"map" == "map" )
{
s = size((map)resource_data);
}
else
{
s = size((list)resource_data);
}
}
if ( s != 0 || tomerge != "")
{
if (size(tomerge) > 0 )
{
integer i = 0;
string tomergetypes = d["X-SuSE-YaST-AutoInstMergeTypes"]:"";
list MergeTypes = splitstring(tomergetypes, ",");
foreach( string res, (list<string>)splitstring(tomerge, ",") , ``{
if ( MergeTypes[i]:"map" == "map")
{
map<string,any> rd = (map<string,any>)resource_data;
current[res] = rd[res]:$[];
} else {
map<string,any> rd =(map<string,any>)resource_data;
current[res] = rd[res]:[];
}
i = i + 1;
});
} else {
current[resource] = resource_data;
}
}
else if (s == 0 )
{
e = add ( e, resource);
}
}
});
foreach(string k, any v, current, ``{
if (!haskey(current, k) && !contains(e, k ))
current[k] = v;
});
Popup::ClearFeedback();
prepare = false;
return;
}
/**
* Reset profile to initial status
* @return void
*/
global define void Reset ()
{
y2milestone("Resetting profile contents");
current = $[];
return;
}
/**
* Save YCP data into XML
* @param path to file
* @return boolean true on success
*/
global define boolean Save (string file)
{
Prepare();
boolean ret = false;
y2debug("Saving data (%1) to XML file %2", current, file);
if( AutoinstConfig::ProfileEncrypted ) {
string xml = XML::YCPToXMLString(`profile, current);
if( size(xml) > 0 ) {
if( AutoinstConfig::ProfilePassword == "" ) {
string p = "";
string q = "";
do {
UI::OpenDialog(
`VBox(
`Label(_("Encrypted AutoYaST profile. Please enter the password twice")),
`Password(`id(`password), ""),
`Password(`id(`password2), ""),
`PushButton(`id(`ok), Label::OKButton())
)
);
any button = nil;
repeat {
button = UI::UserInput();
p = (string) UI::QueryWidget(`id(`password), `Value);
q = (string) UI::QueryWidget(`id(`password2), `Value);
} until ( button == `ok );
UI::CloseDialog();
} while( p != q );
AutoinstConfig::ProfilePassword = AutoinstConfig::ShellEscape((string)p);
}
string dir = (string)SCR::Read(.target.tmpdir);
string command = sformat("gpg2 -c --armor --batch --passphrase \"%1\" --output %2/encrypted_autoyast.xml",
AutoinstConfig::ProfilePassword,dir);
SCR::Execute(.target.bash_input, command, xml);
if( SCR::Read(.target.size, dir+"/encrypted_autoyast.xml") > 0 ) {
command = sformat( "mv %1/encrypted_autoyast.xml %2", dir, file );
SCR::Execute(.target.bash, command, $[]);
ret = true;
}
}
} else {
ret = XML::YCPToXMLFile(`profile, current, file);
}
return ret;
}
/**
* Save sections of current profile to separate files
*
* @param dir - directory to store section xml files in
* @return - list of filenames
*/
global define map<string,string> SaveSingleSections( string dir ){
Prepare();
y2milestone("Saving data (%1) to XML single files", current);
map<string,string> sectionFiles = $[];
foreach( string sectionName, any section, current, {
string sectionFileName = dir + "/" + sectionName + ".xml";
map<string,any> tmpProfile = $[ sectionName : section ];
if(XML::YCPToXMLFile(`profile, tmpProfile, sectionFileName)){
y2milestone( "Wrote section %1 to file %2", sectionName, sectionFileName );
sectionFiles = add(sectionFiles, sectionName, sectionFileName);
}
else{
y2error(sformat(_("Couldn't write section %1 to file %2"), sectionName, sectionFileName));
}
});
return sectionFiles;
}
/**
* Save the current data into a file to be read after a reboot.
* @param -
* @return true on success
* @see Restore()
*/
global define boolean SaveProfileStructure ( string parsedControlFile)
{
y2milestone("Saving control file in YCP format");
return SCR::Write( .target.ycp, parsedControlFile, current );
}
/**
* Read YCP data as the control file
* @param ycp file
* @return boolean
*/
global define boolean ReadProfileStructure ( string parsedControlFile )
{
current = (map<string,any>) SCR::Read( .target.ycp, [ parsedControlFile, $[]] );
if (current == $[])
return false;
else
Import (current);
return true;
}
/**
* General compatibility issues
* @param current profile
* @return map converted profile
*/
define map<string, any> Compat(map<string, any> _current)
{
// scripts
if ( haskey(_current, "pre-scripts") ||
haskey(_current, "post-scripts") ||
haskey(_current, "chroot-scripts") )
{
list pre = _current["pre-scripts"]:[];
list post = _current["post-scripts"]:[];
list chroot = _current["chroot-scripts"]:[];
map scripts = $[
"pre-scripts":pre,
"post-scripts":post,
"chroot-scripts":chroot
];
_current = remove(_current, "pre-scripts");
_current = remove(_current, "post-scripts");
_current = remove(_current, "chroot-scripts");
_current["scripts"] = scripts;
}
// general
boolean old = false;
map<string, any> general_options
= _current["general"]:$[];
map security = _current["security"]:$[];
map report = _current["report"]:$[];
foreach(string k, any v, general_options , ``{
if (k == "keyboard" && is(v, string))
old = true;
else if (k == "mouse" && is(v, string))
old = true;
else if (k == "encryption_method")
old = true;
else if (k == "timezone" && is(v, string))
old = true;
});
map new_general = $[];
if (old) {
y2milestone("Old format, converting.....");
new_general["language"] = general_options["language"]:"";
map keyboard = $[];
keyboard["keymap"] = general_options["keyboard"]:"";
new_general["keyboard"] = keyboard;
map clock = $[];
clock["timezone"] = general_options["timezone"]:"";
if ( general_options["hwclock"]:"" == "localtime")
{
clock["hwclock"] = "localtime";
}
else if ( general_options["hwclock"]:"" == "GMT")
{
clock["hwclock"] = "GMT";
}
new_general["clock"] = clock;
map mode = $[];
if (haskey(general_options, "reboot")) {
mode["reboot"] = general_options["reboot"]:false;
}
if (haskey(report, "confirm")) {
mode["confirm"] = report["confirm"]:false;
report = remove(report, "confirm");
}
new_general["mode"] = mode;
if (haskey(general_options, "encryption_method"))
{
security["encryption"] = general_options["encryption_method"]:"";
}
map net = _current["networking"]:$[];
list<map<string, string> > ifaces = net["interfaces"]:[];
list newifaces = maplist(map<string, string> iface , ifaces, ``{
map newiface = mapmap(string k, string v, iface, ``{
return ($[tolower(k): v]);
});
return newiface;
});
net["interfaces"] = newifaces;
_current["general"] = new_general;
_current["report"] = report;
_current["security"] = security;
_current["networking"] = net;
}
return _current;
}
/**
* Read XML into YCP data
* @param path to file
* @return boolean
*/
global define boolean ReadXML (string file) {
// does not work here
//if ( !FileUtils::Exists( file ) )
// return false;
string tmp = (string)SCR::Read (.target.string, file);
list<string> l = splitstring (tmp, "\n");
if( tmp != nil && l[0]:"" == "-----BEGIN PGP MESSAGE-----" ) {
map<string,any> out = $[];
while( out["stdout"]:"" == "" ) {
UI::OpenDialog(
`VBox(
`Label(_("Encrypted AutoYaST profile. Please enter the correct password.")),
`Password(`id(`password), ""),
`PushButton(`id(`ok), Label::OKButton())
)
);
string p = "";
any button = nil;
repeat {
button = UI::UserInput();
p = (string) UI::QueryWidget(`id(`password), `Value);
} until ( button == `ok );
UI::CloseDialog();
string command = sformat("gpg2 -d --batch --passphrase \"%1\" %2",p, file);
out = (map<string,any>)SCR::Execute(.target.bash_output, command, $[]);
}
current = XML::XMLToYCPString( out["stdout"]:"" );
AutoinstConfig::ProfileEncrypted = true;
// FIXME: rethink and check for sanity of that!
// save decrypted profile for modifying pre-scripts
if( Stage::initial() )
//SCR::Write(.target.string, AutoinstConfig::profile_dir+"/autoinst.xml", out["stdout"]:"");
SCR::Write(.target.string, file, out["stdout"]:"");
} else {
y2debug("Reading %1", file);
current = XML::XMLToYCPFile(file);
}
if ( current != $[] && size(current) == 0 )
{
// autoyast has read the autoyast configuration file but something went wrong
string message = _("The XML parser reported an error while parsing the autoyast profile. The error message is:\n");
message = message + XML::XMLError();
Popup::Error ( message );
return (false);
}
Import (current);
return (true);
}
define map<string,any> setMValue( list<any> l, any v, map<string,any> m );
define list<any> setLValue( list<any> l, any v, list<any> m );
/*
setMValue together with setLValue are helper functions for
setElementyList
*/
map<string,any> setMValue( list<any> l, any v, map<string,any> m ) {
string i = (string)l[0]:"";
list<any> tmp = remove(l,0);
if( size(tmp) > 0 ) {
if( is( tmp[0]:nil, string ) ) {
m[i] = setMValue( tmp, v, m[i]:$[] );
} else {
m[i] = setLValue( tmp, v, m[i]:[] );
}
} else {
y2debug("setting %1 to %2",i,v);
m[i] = v;
}
return m;
}
list<any> setLValue( list<any> l, any v, list<any> m ) {
integer i = (integer)l[0]:0;
list<any> tmp = remove(l,0);
if( size(tmp) > 0 ) {
if( is( tmp[0]:nil, string ) ) {
m[i] = setMValue( tmp, v, m[i]:$[] );
} else {
m[i] = setLValue( tmp, v, m[i]:[] );
}
} else {
y2debug("setting %1 to %2",i,v);
m[i] = v;
}
return m;
}
/**
* this function is a replacement for this code:
* list<any> l = [ "key1",0,"key3" ];
* m[ l ] = v;
* @return map
*/
global define map<string,any> setElementByList( list<any> l, any v, map<string,any> m ) {
m = setMValue( l, v, m );
return m;
}
global void checkProfile() {
string file = AutoinstConfig::tmpDir + "/" + "valid.xml";
Save(file);
string summary = "Some schema check failed!\nPlease attach your logfile to bug id 211014\n\n";
boolean valid = true;
list<list> validators = [
[
_("Checking XML with RNG validation..."),
"/usr/bin/xmllint --noout --relaxng "
+ Directory::schemadir + "/autoyast/rng/profile.rng", ""
]
];
if ( ! Stage::cont() && PackageSystem::Installed("jing") ) {
validators = add( validators, [
_("Checking XML with RNC validation..."),
"/usr/bin/jing >&2 -c "
+ Directory::schemadir + "/autoyast/rnc/profile.rnc", "jing_sucks"
] );
}
foreach (list i, validators, {
string header = i[0]:"";
string cmd = i[1]:"" + " " + file;
summary = summary + header + "\n";
map o = (map)SCR::Execute (.target.bash_output, cmd);
y2debug("validation output: %1", o);
summary = summary + cmd + "\n";
summary = summary + o["stderr"]:"" + "\n";
summary = summary + "\n";
if( o["exit"]:1 != 0 || (i[2]:"" == "jing_sucks" && size(o["stderr"]:"") > 0 ) )
valid = false;
});
if( ! valid ) {
Popup::Error(summary);
y2milestone("Profile check failed please attach the log to bug id 211014: %1",summary);
}
}
}
ACC SHELL 2018