ACC SHELL
/**
* File: modules/AutoinstSoftware.ycp
* Package: Autoyast
* Summary: Software
* Authors: Anas Nashif <nashif@suse.de>
*
* $Id: AutoinstSoftware.ycp 61894 2010-04-28 09:36:35Z ug $
*
*/
{
module "AutoinstSoftware";
textdomain "autoinst";
import "Profile";
import "Summary";
import "Stage";
import "SpaceCalculation";
import "Packages";
import "Popup";
import "Report";
import "Kernel";
import "AutoinstConfig";
import "ProductControl";
import "Storage";
import "Mode";
import "Misc";
import "Directory";
include "autoinstall/io.ycp";
// All shared data are in yast2.rpm to break cyclic dependencies
import "AutoinstData";
import "PackageAI";
global map Software = $[];
global map<string, any> image = $[];
global string image_arch = "";
// patterns
global list<string> patterns = [];
// Kernel, force type of kernel to be installed
global string kernel = "";
// Packages that should be installed in continue mode
// AutoinstData::post_packages = [];
global string ft_module = "";
/* Enable Imaging */
global boolean imaging = false;
/* default value of settings modified */
global boolean modified = false;
global list<string> inst = [];
global list<map<string,any> > all_xpatterns = [];
global string instsource = "";
global string isolinuxcfg = "";
/**
* 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;
}
/**
* Import data
* @param settings settings to be imported
* @return true on success
*/
global define boolean Import(map settings)
{
Software = settings;
patterns = settings["patterns"]:[];
instsource = settings["instsource"]:"";
string notFound = "";
/* what is this good for? disturbs the main-repo selection */
/*
Packages::Init(true);
Packages::InitializeAddOnProducts();
*/
list<string> packagesAvailable = Pkg::GetPackages (`available, true);
list<string> patternsAvailable = [];
list<map<string,any> > allPatterns = Pkg::ResolvableDependencies ("", `pattern, "");
allPatterns = filter( map<string,any> m, allPatterns, ``{
if( m["user_visible"]:false )
patternsAvailable = add( patternsAvailable, m["name"]:"" );
return m["user_visible"]:false;
});
list<string> regexFound = [];
settings["packages"] = (list<string>)filter( string want_pack, (list<string>)settings["packages"]:[], ``{
if( !issubstring( want_pack, "/" ) )
return true;
want_pack = deletechars( want_pack, "/" );
foreach( string pack, packagesAvailable, ``{
y2milestone("matching %1 against %2", pack, want_pack);
if( regexpmatch (pack, want_pack) ) {
regexFound = add(regexFound, pack);
y2milestone("match");
}
});
return false;
});
settings["packages"] = (list<string>)union( settings["packages"]:[], regexFound );
regexFound = [];
patterns = (list<string>)filter( string want_patt, patterns, ``{
if( !issubstring( want_patt, "/" ) )
return true;
want_patt = deletechars( want_patt, "/" );
foreach( string patt, patternsAvailable, ``{
y2milestone("matching %1 against %2", patt, want_patt);
if( regexpmatch (patt, want_patt) ) {
regexFound = add(regexFound, patt);
y2milestone("match");
}
});
return false;
});
patterns = (list<string>)union( patterns, regexFound );
foreach( string pack, settings["packages"]:[], ``{
if( ! Pkg::IsAvailable(pack) && Stage::initial() ) {
notFound = notFound + pack + "\n";
}
});
if( size(notFound) > 0 ) {
y2error("packages not found: %1",notFound);
// warning text during the installation. %1 is a list of package names
Report::Error( sformat(_("These packages could not be found in the software repositories:\n%1"), notFound));
}
PackageAI::toinstall = settings["packages"]:[];
kernel = settings["kernel"]:"";
AutoinstData::post_packages = settings["post-packages"]:[];
AutoinstData::post_patterns = settings["post-patterns"]:[];
PackageAI::toremove = settings["remove-packages"]:[];
/* Imaging */
/*
map<string, any> image = settings["system_images"]:$[];
imaging = image["enable_multicast_images"]:false;
ft_module = image["module_name"]:"";
if (settings == $[])
modified = false;
else
modified = true;
*/
image = settings["image"]:$[];
if( image["image_location"]:"" != "" && image["image_name"]:"" != "" )
imaging=true;
return true;
}
/**
* Constructer
*/
global define void AutoinstSoftware()
{
if ( Stage::cont () && Mode::autoinst ())
{
Pkg::TargetInit ("/", false);
Import(Profile::current["software"]:$[]);
}
return;
}
define string GetArchOfELF (string filename)
{
map bash_out = (map) SCR::Execute (.target.bash_output, Directory::ybindir +
"/elf-arch " + filename);
if (bash_out["exit"]:1 != 0)
return "unknown";
return deletechars (bash_out["stdout"]:"unknown", "\n");
}
global define void createImage( string targetdir ) {
string rootdir = (string)SCR::Read(.target.tmpdir);
string zypperCall = "";
string outputRedirect = " 2>&1 >> /tmp/ay_image.log";
boolean finalPopup = ( size(targetdir) == 0 );
image["script_location"] = image["script_location"]:"file:///usr/lib/YaST2/bin//fetch_image.sh";
image["script_params"] = image["script_params"]:[ image["image_location"]:""+"/"+image["image_name"]:"image"+".tar.gz" ];
SCR::Execute (.target.bash, "rm -f /tmp/ay_image.log" );
// bind-mount devices
SCR::Execute (.target.mkdir, rootdir+"/dev" );
integer returnCode = (integer)SCR::Execute (.target.bash, sformat("touch /%1/dev/null %1/dev/zero", rootdir));
returnCode = (integer)SCR::Execute (.target.bash, sformat("mount -o bind /dev/zero /%1/dev/zero", rootdir));
returnCode = (integer)SCR::Execute (.target.bash, sformat("mount -o bind /dev/null /%1/dev/null", rootdir));
// Add Source:
// zypper --root /space/tmp/tmproot/ ar ftp://10.10.0.100/install/SLP/openSUSE-11.2/i386/DVD1/ main
zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive ar %2 main-source %3", rootdir, instsource, outputRedirect );
y2milestone("running %1", zypperCall );
returnCode = (integer)SCR::Execute (.target.bash, zypperCall);
if( returnCode != 0 && returnCode != 4 ) {
// 4 means "already exists"
Popup::Error( sformat( _("Adding repo %1 failed"), instsource) );
}
// Add add-ons
map addOnExport = (map)WFM::CallFunction("add-on_auto", ["Export"]);
list<map> addOns = addOnExport["add_on_products"]:[];
foreach( map addOn, addOns, ``{
zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive ar %2 %3 %4",
rootdir, addOn["media_url"]:"", addOn["product"]:"", outputRedirect );
returnCode = (integer)SCR::Execute (.target.bash, zypperCall);
if( returnCode != 0 && returnCode != 4 ) {
Popup::Error( sformat( _("Adding repo %1 failed"), addOn["product"]:"") );
}
});
// Install
zypperCall = sformat("ZYPP_READONLY_HACK=1 zypper --root %1 --gpg-auto-import-keys --non-interactive install --auto-agree-with-licenses ", rootdir);
string pattern = mergestring(patterns, " ");
Popup::ShowFeedback( "Creating Image - installing patterns", "");
y2milestone("installing %1", pattern);
returnCode = (integer)SCR::Execute (.target.bash, zypperCall+"-t pattern " + pattern + outputRedirect);
Popup::ClearFeedback();
if( returnCode != 0 ) {
Popup::Error( _("Image creation failed while pattern installation. Please check /tmp/ay_image.log") );
}
if( size(PackageAI::toinstall) > 0 ) {
string package = mergestring( PackageAI::toinstall, " ");
Popup::ShowFeedback( _("Creating Image - installing packages"), "");
returnCode = (integer)SCR::Execute (.target.bash, zypperCall+" "+package + outputRedirect);
Popup::ClearFeedback();
if( returnCode != 0 ) {
Popup::Error( _("Image creation failed while package installation. Please check /tmp/ay_image.log") );
}
}
image_arch = GetArchOfELF( sformat("%1/bin/bash",rootdir) );
y2milestone("Image architecture = %1", image_arch);
if( targetdir == "" ) {
// Popup::Message( _("in the next file dialog you have to choose the target directory to save the image") );
targetdir = UI::AskForExistingDirectory ( "/", _("Store image to ...") );
}
// umount devices
returnCode = (integer)SCR::Execute (.target.bash, sformat("umount %1/dev/null %1/dev/zero %1/proc", rootdir));
returnCode = (integer)SCR::Execute (.target.bash, sformat("rm -rf %1/dev", rootdir));
// Compress image:
// tar cfz /srv/www/htdocs/image.tar.gz --exclude="proc*" .
string tarCommand = sformat("tar cfvz %4/%3.tar.gz --exclude=\"./proc*\" --exclude=\"/%3.tar.gz\" -C %1 . %2",
rootdir, outputRedirect, image["image_name"]:"", targetdir);
y2milestone("running %1", tarCommand);
Popup::Message( sformat( _("You can do changes to the image now in %1/\nIf you press the ok-button, the image will be compressed and can't be changed anymore."), rootdir ) );
Popup::ShowFeedback( "Compressing Image ...", "" );
returnCode = (integer)SCR::Execute (.target.bash, tarCommand);
Popup::ClearFeedback();
if( returnCode != 0 ) {
Popup::Error( sformat( _("Image compressing failed in '%1'. Please check /tmp/ay_image.log"), rootdir ) );
}
if( finalPopup )
Popup::Message( _("Image created successfully") );
}
global define boolean copyFiles4ISO( string target ) {
boolean ret = true;
string copy = Misc::SysconfigRead( .sysconfig.autoinstall.COPY_FOR_ISO,
"/,/boot/,/boot/__ARCH__/,/boot/__ARCH__/loader/,/media.1/,/suse/setup/descr/");
list<string> copyList = splitstring(copy,",");
foreach( string source, copyList, ``{
if( issubstring( source, "__ARCH__" ) )
source = regexpsub( source, "(.*)__ARCH__(.*)", sformat("\\1%1\\2", image_arch) );
if( substring( source, size(source)-1 ) == "/" ) {
// copy a directory (ends with / in directory.yast)
SCR::Execute (.target.mkdir, target+"/"+source );
if( !GetURL( instsource+"/"+source+"directory.yast", "/tmp/directory.yast" ) ) {
Popup::Error( sformat(_("can not get the directory.yast file at `%1`.\nYou can create that file with 'ls -F > directory.yast' if it's missing."), instsource+"/"+source));
ret = false;
break;
}
// directory.yast is our filelist
string files = (string)SCR::Read(.target.string, "/tmp/directory.yast");
list<string> filesInDir = splitstring( files, "\n" );
foreach( string file, filesInDir, ``{
// don't copy subdirs. They have to be mentioned explicit. Copy only files from that dir.
y2milestone("will get %1 from %2 to %3", file, instsource+"/"+source, target );
if( size(file) > 0 && substring( file, size(file)-1 ) != "/" )
while( ret && !GetURL( instsource+"/"+source+file, target+"/"+source+"/"+file ) ) {
if( !Popup::YesNo( sformat(_("can not read '%1'. Try again?"), instsource+"/"+source+file ) ) )
ret = false;
}
if( !ret )
break;
});
} else {
// copy a file
if( !GetURL( instsource+"/"+source, target+"/"+source ) ) {
Popup::Error( sformat(_("can not read '%1'. ISO creation failed"), instsource+"/"+source) );
ret = false;
break;
}
}
});
// lets always copy an optional(!) driverupdate file. It's very unlikely that it's in directory.yast
GetURL( instsource+"/driverupdate", target+"/driverupdate" );
SCR::Execute (.target.bash, sformat("cp /usr/lib/YaST2/bin/fetch_image.sh %1/",target) );
return ret;
}
global define void createISO() {
/* we will have the image.tar.gz and autoinst.xml on the root of the DVD/CD */
string isodir = "/tmp/ay_iso/";
SCR::Execute (.target.bash, sformat("rm -rf %1", isodir) );
SCR::Execute (.target.mkdir, isodir );
string outputRedirect = " 2>&1 >> /tmp/ay_image.log";
integer returnCode = 0;
createImage( isodir );
Popup::ShowFeedback( _("Preparing ISO Filestructure ..."), "" );
copyFiles4ISO( isodir );
Popup::ClearFeedback();
// prepare and save autoinst.xml
image["image_location"] = "file:///";
image["script_params"] = [ image["image_location"]:"" + "/" + image["image_name"]:"" + ".tar.gz" ];
image["script_location"] = "file:///fetch_image.sh";
Profile::Save( sformat("%1/autoinst.xml", isodir) );
// prepare and save isolinux.cfg boot menu of the media
isolinuxcfg = (string)SCR::Read(.target.string, sformat("%1/boot/%2/loader/isolinux.cfg", isodir, image_arch));
list<string> lines = splitstring( isolinuxcfg, "\n" );
lines = maplist( string l, lines, {
if( issubstring( l, " append " ) )
l = l + " autoyast=file:///autoinst.xml";
return l;
});
isolinuxcfg = mergestring( lines, "\n" );
UI::OpenDialog(
`opt( `decorated ),
`VBox(
`HBox( `VSpacing(14),
`MultiLineEdit(`id(`isolinuxcfg), _("boot config for the DVD"), isolinuxcfg )
),
`PushButton( `id( `create_image ), `opt( `default, `hstretch ), _("Ok") ),
`Label( sformat( _("You can do changes to the ISO now in %1, like adding a complete different AutoYaST XML file.\nIf you press the ok-button, the iso will be created."), isodir) )
)
);
UI::UserInput();
isolinuxcfg = (string)UI::QueryWidget(`isolinuxcfg, `Value);
UI::CloseDialog();
SCR::Write(.target.string, sformat("%1/boot/%2/loader/isolinux.cfg", isodir, image_arch), isolinuxcfg);
// create the actual ISO file
// Popup::Message( _("Please choose a place where you want to save the ISO file in the next dialog") );
string targetdir = UI::AskForExistingDirectory ( "/", _("Store ISO image to ...") );
Popup::ShowFeedback( _("Creating ISO File ..."), "" );
string cmd = sformat("mkisofs -o %1/%2.iso -R -b boot/%3/loader/isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table %4",
targetdir, image["image_name"]:"", image_arch, isodir);
y2milestone("executing %1", cmd + outputRedirect);
returnCode = (integer)SCR::Execute (.target.bash, cmd);
Popup::ClearFeedback();
if( returnCode != 0 ) {
Popup::Error( sformat("ISO creation failed in '%1'. Please check /tmp/ay_image.log", isodir ) );
} else {
Popup::Message( sformat(_("ISO successfully created at %1"), "/tmp/"+image["image_name"]:""+".iso" ) );
}
}
/**
* Export data
* @return dumped settings (later acceptable by Import())
*/
global define map Export()
{
map s = $[];
if (kernel != "")
s["kernel"] = kernel ;
if( patterns != [])
s["patterns"] = patterns;
if (PackageAI::toinstall != [])
s["packages"] = PackageAI::toinstall;
if (AutoinstData::post_packages != [])
s["post-packages"] = AutoinstData::post_packages ;
if (PackageAI::toremove != [])
s["remove-packages"] = PackageAI::toremove;
s["instsource"] = instsource;
s["image"] = image;
return (s);
}
/**
* Add packages needed by modules, i.e. NIS, NFS etc.
* @param list of strings packages to add
* @return void
*/
global define void AddModulePackages(list<string> module_packages)
{
PackageAI::toinstall = toset((list<string>)
union(PackageAI::toinstall, module_packages));
//
// Update profile
//
Profile::current["software"] = Export();
return;
}
/**
* Remove packages not needed by modules, i.e. NIS, NFS etc.
* @param list of packages to remove
* @return void
*/
global define void RemoveModulePackages (list<string> module_packages)
{
PackageAI::toinstall = filter(string p, PackageAI::toinstall, ``(!contains(module_packages,p)));
Profile::current["software"] = Export();
return;
}
/**
* Summary
* @return Html formatted configuration summary
*/
global define string Summary()
{
string summary = "";
summary = Summary::AddHeader(summary, _("Selected Patterns"));
if (size( patterns ) > 0 )
{
summary = Summary::OpenList(summary);
foreach(string a, patterns, ``{
summary = Summary::AddListItem(summary, a);
});
summary = Summary::CloseList(summary);
}
else
{
summary = Summary::AddLine(summary, Summary::NotConfigured());
}
summary = Summary::AddHeader(summary, _("Individually Selected Packages"));
summary = Summary::AddLine(summary, sformat("%1",
size(PackageAI::toinstall)));
summary = Summary::AddHeader(summary, _("Packages to Remove"));
summary = Summary::AddLine(summary, sformat("%1",
size(PackageAI::toremove)));
if (kernel != "")
{
summary = Summary::AddHeader(summary, _("Force Kernel Package"));
summary = Summary::AddLine(summary, sformat("%1", kernel));
}
return summary;
}
/**
* Compute list of packages selected by user and other packages needed for important
* configuration modules.
* @return list of strings list of packages needed for autoinstallation
*/
global define list<string> autoinstPackages()
{
list<string> allpackages = [];
// the primary list of packages
allpackages = (list<string>) union (allpackages, PackageAI::toinstall);
// In autoinst mode, a kernel should not be available
// in <packages>
if ( size(kernel) == 0)
{
list <string> kernel_pkgs = Kernel::ComputePackages ();
allpackages = (list <string>) union (allpackages, kernel_pkgs);
}
else
{
if (Pkg::IsAvailable (kernel))
{
allpackages = add (allpackages, kernel);
string kernel_nongpl = kernel + "-nongpl";
if (Pkg::IsAvailable (kernel_nongpl))
allpackages = add (allpackages, kernel_nongpl);
}
else
{
y2warning ("%1 not available, using kernel-default", kernel);
list <string> kernel_pkgs = Kernel::ComputePackages ();
allpackages = (list <string>) union (allpackages, kernel_pkgs);
}
}
return allpackages;
}
/**
* Configure software settings
* @param void
* @return boolean
*/
global define boolean Write()
{
if (imaging)
{
if( ! image["run_kickoff"]:false )
ProductControl::DisableModule ("kickoff");
ProductControl::DisableModule ("rpmcopy");
return true;
}
boolean ok = true;
Packages::Init(true);
list<string> failed = [];
/* switch for recommended patterns installation (workaround for our very weird pattern design) */
if( Profile::current["software","install_recommended"]:false == false ) {
/* set SoftLock to avoid the installation of recommended patterns (#159466) */
foreach( map<string,any> p, Pkg::ResolvableProperties ("", `pattern, ""), ``{
Pkg::ResolvableSetSoftLock( p["name"]:"", `pattern );
});
}
foreach(string p, toset(patterns),
``{
if (! Pkg::ResolvableInstall( p, `pattern ) )
{
failed = add(failed, p);
}
});
if (size(failed) > 0 )
{
y2error("Error while setting pattern: %1", mergestring(failed, ","));
Report::Warning(sformat(_("Could not set patterns: %1."), mergestring(failed, ",")));
}
list<string> autoinstPacks = autoinstPackages ();
// FIXME: optimization for package list evaluation turned off because it optimized it
// into an unbootable state (no kernel) bnc#427731
//
// list<string> autoinstPacks = PackageAI::toinstall;
y2milestone("Packages selected in autoinstall mode: %1", autoinstPacks);
if (size(autoinstPacks) > 0 )
{
y2milestone("Installing individual packages: %1", Pkg::DoProvide(autoinstPacks) );
}
list<string> computed_packages = Packages::ComputeSystemPackageList();
y2debug("Computed list of packages: %1", computed_packages);
foreach( string pack, computed_packages, ``{
if( size(kernel) > 0 && pack != kernel && search(pack, "kernel-") == 0 ) {
y2milestone("taboo for kernel %1",pack);
PackageAI::toremove = add( PackageAI::toremove, pack );
}
});
//
// Now remove all packages listed in remove-packages
//
y2milestone("Packages to be removed: %1", PackageAI::toremove);
if (size(PackageAI::toremove) > 0 )
{
foreach (string rp, PackageAI::toremove, ``{
//Pkg::ResolvableSetSoftLock( rp, `package ); // FIXME: maybe better Pkg::PkgTaboo(rp) ?
Pkg::PkgTaboo(rp);
});
Pkg::DoRemove(PackageAI::toremove);
}
list<string> pack = Storage::AddPackageList();
if( size(pack)>0 )
{
y2milestone("Installing storage packages: %1", Pkg::DoProvide( pack ));
}
//
// Solve dependencies
//
if( !Pkg::PkgSolve(false) ) {
Report::Error( _("The package resolver run failed. Please check your software section in the autoyast profile.") );
}
SpaceCalculation::ShowPartitionWarning();
return (ok);
}
/**
* Initialize temporary target
*/
global define void pmInit()
{
// string tmproot = AutoinstConfig::tmpDir;
// SCR::Execute(.target.mkdir, tmproot + "/root");
// Pkg::TargetInit( tmproot + "/root", true);
// Pkg::TargetInit( "/", true);
Pkg::TargetInit( (string)SCR::Read(.target.tmpdir), true );
y2milestone("SourceStartCache: %1", Pkg::SourceStartCache(false));
return;
}
/**
* Add post packages
* @param list calculated post packages
* @return void
*/
global define void addPostPackages(list<string> calcpost)
{
AutoinstData::post_packages = (list<string>)toset(union(calcpost,AutoinstData::post_packages));
return;
}
/**
* Return list of software packages of calling client
* @return map map of installed software package
* "patterns" -> list<string> addon selections
* "packages" -> list<string> user selected packages
* "remove-packages" -> list<string> packages to remove
*/
global define map<string, any > ReadHelper()
{
boolean ret = Pkg::TargetInit("/", false);
// Pkg::TargetInitialize ("/");
Pkg::TargetLoad();
Pkg::SourceStartManager (true);
Pkg::PkgSolve(false);
inst = Pkg::GetPackages(`installed, true);
list<map<string,any> > all_patterns = Pkg::ResolvableProperties ("", `pattern, "");
all_xpatterns = Pkg::ResolvableDependencies ("", `pattern, "");
list<string> patterns = [];
list<string> visible_patterns = [];
list<map> patternsFullData = filter( map p, all_patterns, ``{
boolean ret = false;
if( p["status"]:`none == `installed && !contains( patterns, p["name"]:"no name" ) ) {
patterns = add( patterns, p["name"]:"no name" );
ret = true;
if( p["user_visible"]:true == true )
visible_patterns = add( visible_patterns, p["name"]:"no name" );
}
return ret;
});
Pkg::TargetFinish ();
string tmproot = AutoinstConfig::tmpDir;
SCR::Execute(.target.mkdir, tmproot + "/rootclone");
Pkg::TargetInit( tmproot + "/rootclone", true);
y2debug("SourceStartCache: %1", Pkg::SourceStartCache(false));
Pkg::SourceStartManager(true);
list<string> userpackages = Pkg::FilterPackages(false, false, true, true);
Pkg::TargetFinish ();
// Remove kernel packages
list<string> removepackages = [];
list<string> patternPackages = [];
list<string> new_p = [];
foreach( string tmp_pattern, patterns, ``{
list<map<string,any> > xpattern = filter( map<string,any> p, all_xpatterns, ``( p["name"]:"" == tmp_pattern ) );
map<string,any> found = xpattern[0]:$[];
boolean req = false;
// kick out hollow patterns (always fullfilled patterns)
foreach( map<string,any> d, found["dependencies"]:[], ``{
if( d["res_kind"]:"" == "package" && (d["dep_kind"]:"" == "requires" || d["dep_kind"]:"" == "recommends") ) {
patternPackages = add(patternPackages, d["name"]:"");
req = true;
}
});
// workaround for our pattern design
// a pattern with no requires at all is always fullfilled of course
// you can fullfill the games pattern with no games installed at all
if( req == true )
new_p = add( new_p, tmp_pattern );
});
patterns = new_p;
list<string> patternPackagesTemp = patternPackages;
boolean run = false;
integer emergency_break = 0;
do {
run = false;
emergency_break = emergency_break + 1;
/* remove all packages that are pulled in by the resolver anyway */
list<string> tmp = [];
patternPackagesTemp = toset( patternPackagesTemp );
foreach( string ppackage, patternPackagesTemp, ``{
foreach( map<string,any> d, Pkg::ResolvableDependencies (ppackage, `package, ""), ``{
foreach( map<string,any> dd, d["dependencies"]:[], ``{
if( dd["res_kind"]:"" == "package" && (dd["dep_kind"]:"" == "requires" || dd["dep_kind"]:"" == "recommends") && !contains(patternPackages , dd["name"]:"") ) {
patternPackages = add(patternPackages, dd["name"]:"");
tmp = add(tmp, dd["name"]:"");
run = true;
}
});
});
});
patternPackagesTemp = tmp;
y2milestone("temp package list = %1", tmp);
} while( run == true && emergency_break < 20 );
map<string, any > software = $[];
if( size(patterns) > 0 ) {
foreach(string p, inst, ``{
if (!contains(patternPackages, p))
userpackages = add( userpackages, p );
});
foreach(string p, patternPackages, ``{
if (!contains(inst,p))
removepackages = add( removepackages, p );
});
}
software["packages"] = sort( filter(string pkg, userpackages, ``(! regexpmatch(pkg, "kernel-.*") || pkg == "kernel-uml")) );
software["patterns"] = sort( visible_patterns );
software["remove-packages"] = toset( removepackages );
return software;
}
global define boolean Read(){
return Import( ReadHelper() );
}
//EOF
}
ACC SHELL 2018