Imported Upstream version 17.25.4
[platform/upstream/libzypp.git] / zypp / solver / detail / Testcase.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/solver/detail/Testcase.cc
10  *
11 */
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <streambuf>
16 #include <boost/iterator/function_output_iterator.hpp>
17
18 #define ZYPP_USE_RESOLVER_INTERNALS
19
20 #include <zypp/solver/detail/Testcase.h>
21 #include <zypp/base/Logger.h>
22 #include <zypp/base/LogControl.h>
23 #include <zypp/base/GzStream.h>
24 #include <zypp/base/String.h>
25 #include <zypp/base/PtrTypes.h>
26 #include <zypp/base/NonCopyable.h>
27 #include <zypp/base/ReferenceCounted.h>
28
29 #include <zypp/AutoDispose.h>
30 #include <zypp/ZConfig.h>
31 #include <zypp/PathInfo.h>
32 #include <zypp/ResPool.h>
33 #include <zypp/Repository.h>
34 #include <zypp/VendorAttr.h>
35 #include <zypp/target/modalias/Modalias.h>
36
37 #include <zypp/sat/detail/PoolImpl.h>
38 #include <zypp/solver/detail/Resolver.h>
39 #include <zypp/solver/detail/SystemCheck.h>
40
41 #include <yaml-cpp/yaml.h>
42
43 extern "C" {
44 #include <solv/testcase.h>
45 }
46
47 using std::endl;
48
49 /////////////////////////////////////////////////////////////////////////
50 namespace zypp
51 { ///////////////////////////////////////////////////////////////////////
52   ///////////////////////////////////////////////////////////////////////
53   namespace solver
54   { /////////////////////////////////////////////////////////////////////
55     /////////////////////////////////////////////////////////////////////
56     namespace detail
57     { ///////////////////////////////////////////////////////////////////
58
59       //---------------------------------------------------------------------------
60
61       Testcase::Testcase()
62         :dumpPath("/var/log/YaST2/solverTestcase")
63       {}
64
65       Testcase::Testcase(const std::string & path)
66         :dumpPath(path)
67       {}
68
69       Testcase::~Testcase()
70       {}
71
72       bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
73       {
74         MIL << "createTestcase at " << dumpPath << (dumpPool?" dumpPool":"") << (runSolver?" runSolver":"") << endl;
75         PathInfo path (dumpPath);
76
77         if ( !path.isExist() ) {
78           if (zypp::filesystem::assert_dir (dumpPath)!=0) {
79             ERR << "Cannot create directory " << dumpPath << endl;
80             return false;
81           }
82         } else {
83           if (!path.isDir()) {
84             ERR << dumpPath << " is not a directory." << endl;
85             return false;
86           }
87           // remove old stuff if pool will be dump
88           if (dumpPool)
89             zypp::filesystem::clean_dir (dumpPath);
90         }
91
92         if (runSolver) {
93           zypp::base::LogControl::TmpLineWriter tempRedirect;
94           zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
95           zypp::base::LogControl::TmpExcessive excessive;
96
97           resolver.resolvePool();
98         }
99
100         ResPool pool    = resolver.pool();
101         PoolItemList    items_to_install;
102         PoolItemList    items_to_remove;
103         PoolItemList    items_locked;
104         PoolItemList    items_keep;
105
106
107         const std::string slvTestcaseName = "testcase.t";
108         const std::string slvResult       = "solver.result";
109
110         zypp::AutoDispose<const char **> repoFileNames( testcase_mangle_repo_names( resolver.get()->pool ),
111           [ nrepos = resolver.get()->pool->nrepos ]( auto **x ){
112             if (!x) return;
113             for ( int i = 1; i < nrepos; i++ )
114                 solv_free((void *)x[i]);
115             solv_free((void *)x);
116         });
117
118         if ( ::testcase_write( resolver.get(), dumpPath.c_str(), TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, slvTestcaseName.c_str(), slvResult.c_str() ) == 0 ) {
119           ERR << "Failed to write solv data, aborting." << endl;
120           return false;
121         }
122
123         // HACK: directly access sat::pool
124         const sat::Pool & satpool( sat::Pool::instance() );
125
126         YAML::Emitter yOut;
127
128         const auto addTag = [&]( const std::string & tag_r, bool yesno_r = true ){
129           yOut << YAML::Key << tag_r << YAML::Value << yesno_r;
130         };
131
132         yOut << YAML::BeginMap  << YAML::Key << "version" << YAML::Value << "1.0";
133
134         yOut << YAML::Key << "setup" << YAML::Value << YAML::BeginMap;
135
136         yOut << YAML::Key << "channels";
137         yOut << YAML::Value << YAML::BeginSeq;
138
139         std::set<Repository::IdType> repos;
140         for ( const PoolItem & pi : pool ) {
141           if ( pi.status().isToBeInstalled()
142                && !(pi.status().isBySolver())) {
143             items_to_install.push_back( pi );
144           }
145           if ( pi.status().isKept()
146                && !(pi.status().isBySolver())) {
147             items_keep.push_back( pi );
148           }
149           if ( pi.status().isToBeUninstalled()
150                && !(pi.status().isBySolver())) {
151             items_to_remove.push_back( pi );
152           }
153           if ( pi.status().isLocked()
154                && !(pi.status().isBySolver())) {
155             items_locked.push_back( pi );
156           }
157
158           const auto &myRepo = pi.repository();
159           const auto &myRepoInfo = myRepo.info();
160           if ( repos.find( myRepo.id()) == repos.end() ) {
161             repos.insert( myRepo.id() );
162             yOut << YAML::Value << YAML::BeginMap;
163             yOut << YAML::Key << "alias" << YAML::Value << myRepo.alias();
164             yOut << YAML::Key << "url" << YAML::BeginSeq;
165             for ( auto itUrl = myRepoInfo.baseUrlsBegin(); itUrl != myRepoInfo.baseUrlsEnd(); ++itUrl ) {
166               yOut << YAML::Value << itUrl->asString();
167             }
168             yOut << YAML::EndSeq;
169             yOut << YAML::Key << "path" << YAML::Value << myRepoInfo.path().asString();
170             yOut << YAML::Key << "type" << YAML::Value << myRepoInfo.type().asString();
171             yOut << YAML::Key << "generated" << YAML::Value << myRepo.generatedTimestamp().form( "%Y-%m-%d %H:%M:%S" );
172             yOut << YAML::Key << "outdated" << YAML::Value << myRepo.suggestedExpirationTimestamp().form( "%Y-%m-%d %H:%M:%S" );
173             yOut << YAML::Key << "priority" << YAML::Value << myRepoInfo.priority();
174             yOut << YAML::Key << "file" << YAML::Value << str::Format("%1%.repo.gz") % repoFileNames[myRepo.id()->repoid];
175
176             yOut << YAML::EndMap;
177           }
178
179         }
180
181         yOut << YAML::EndSeq;
182
183         yOut << YAML::Key << "arch" << YAML::Value << ZConfig::instance().systemArchitecture().asString() ;
184         yOut << YAML::Key << "solverTestcase" << YAML::Value << slvTestcaseName ;
185         yOut << YAML::Key << "solverResult" << YAML::Value << slvResult ;
186
187         // RequestedLocales
188         const LocaleSet & addedLocales( satpool.getAddedRequestedLocales() );
189         const LocaleSet & removedLocales( satpool.getRemovedRequestedLocales() );
190         const LocaleSet & requestedLocales( satpool.getRequestedLocales() );
191
192         yOut << YAML::Key << "locales" << YAML::Value << YAML::BeginSeq ;
193         for ( Locale l : requestedLocales ) {
194           yOut << YAML::Value << YAML::BeginMap;
195           yOut << YAML::Key << "fate" << YAML::Value << ( addedLocales.count(l) ? "added" : "" ) ;
196           yOut << YAML::Key << "name" << YAML::Value << l.asString() ;
197           yOut << YAML::EndMap;
198         }
199
200         for ( Locale l : removedLocales ) {
201           yOut << YAML::Value << YAML::BeginMap;
202           yOut << YAML::Key << "fate" << YAML::Value << "removed" ;
203           yOut << YAML::Key << "name" << YAML::Value << l.asString() ;
204           yOut << YAML::EndMap;
205         }
206         yOut << YAML::EndSeq; // locales
207
208         // Vendor settings
209         yOut << YAML::Key << "vendors" << YAML::Value << YAML::BeginSeq ;
210         VendorAttr::instance().foreachVendorList( [&]( VendorAttr::VendorList vlist )->bool {
211           if ( ! vlist.empty() ) {
212             yOut << YAML::Value << YAML::BeginSeq;
213             for( const auto & v : vlist )
214               yOut << YAML::Value << v ;
215             yOut << YAML::EndSeq;
216           }
217           return true;
218         } );
219         yOut << YAML::EndSeq; // vendors
220
221         // helper lambda to write a list of elements into a external file instead of the main file
222         const auto &writeListOrFile = [&]( const std::string &name, const auto &list, const auto &callback ) {
223           if ( list.size() > 10 ) {
224             const std::string fName = str::Format("zypp-%1%.yaml") % name;
225             yOut << YAML::Key << name << YAML::Value << fName;
226
227             YAML::Emitter yOutFile;
228             callback( yOutFile, list );
229
230             std::ofstream fout( dumpPath+"/"+fName );
231             fout << yOutFile.c_str();
232           } else {
233             yOut << YAML::Key << name << YAML::Value ;
234             callback( yOut, list );
235           }
236         };
237
238         // AutoInstalled
239         const auto &writeAutoInst = [] ( YAML::Emitter &out, const auto &autoInstalledList ) {
240           out << YAML::BeginSeq;
241           for ( IdString::IdType n : autoInstalledList ) {
242             out << YAML::Value << IdString(n).asString() ;
243           }
244           out << YAML::EndSeq;
245         };
246         writeListOrFile( "autoinst", satpool.autoInstalled(), writeAutoInst );
247
248         // ModAlias
249         const auto &writeModalias = []( YAML::Emitter &out, const auto &modAliasList ){
250           out << YAML::BeginSeq;
251           for ( const auto &modAlias : modAliasList ) {
252             out << YAML::Value << modAlias ;
253           }
254           out << YAML::EndSeq;
255         };
256         writeListOrFile( "modalias", target::Modalias::instance().modaliasList(), writeModalias );
257
258         // Multiversion
259         const auto &writeMultiVersion = [] ( YAML::Emitter &out, const auto &multiversionList ) {
260           out << YAML::BeginSeq;
261           for ( const auto &multiver : multiversionList ) {
262             out << YAML::Value << multiver ;
263           }
264           out << YAML::EndSeq;
265         };
266         writeListOrFile( "multiversion", ZConfig::instance().multiversionSpec(), writeMultiVersion );
267
268
269         yOut << YAML::Key << "resolverFlags" << YAML::Value << YAML::BeginMap;
270         yOut << YAML::Key << "focus" << YAML::Value << asString( resolver.focus() );
271
272         addTag( "ignorealreadyrecommended", resolver.ignoreAlreadyRecommended() );
273         addTag( "onlyRequires",             resolver.onlyRequires() );
274         addTag( "forceResolve",             resolver.forceResolve() );
275
276         addTag( "cleandepsOnRemove",        resolver.cleandepsOnRemove() );
277
278         addTag( "allowDowngrade",           resolver.allowDowngrade() );
279         addTag( "allowNameChange",          resolver.allowNameChange() );
280         addTag( "allowArchChange",          resolver.allowArchChange() );
281         addTag( "allowVendorChange",        resolver.allowVendorChange() );
282
283         addTag( "dupAllowDowngrade",        resolver.dupAllowDowngrade() );
284         addTag( "dupAllowNameChange",       resolver.dupAllowNameChange() );
285         addTag( "dupAllowArchChange",       resolver.dupAllowArchChange() );
286         addTag( "dupAllowVendorChange",     resolver.dupAllowVendorChange() );
287
288
289         yOut << YAML::EndMap; // resolverFlags
290         yOut << YAML::EndMap; // setup
291
292         yOut << YAML::Key << "trials" << YAML::Value << YAML::BeginSeq;
293
294         yOut << YAML::Value << YAML::BeginMap << YAML::Key << "trial" << YAML::Value;
295
296         yOut << YAML::BeginSeq;
297
298         const auto &writeJobsToFile = [&]( const std::string &fName, const auto &data, const auto &cb ){
299           yOut << YAML::Value << YAML::BeginMap;
300           yOut << YAML::Key << "include" << YAML::Value << fName;
301           yOut << YAML::EndMap;
302
303           YAML::Emitter yOutFile;
304           yOutFile << YAML::BeginSeq;
305           cb( yOutFile, data );
306           yOutFile << YAML::EndSeq;
307
308           std::ofstream fout( dumpPath+"/"+fName );
309           fout << yOutFile.c_str();
310         };
311
312         // Multiversion
313         const auto &writePoolItemJobs = []( const std::string &jobName ){
314           return [ &jobName ] ( YAML::Emitter &yOut, const PoolItemList &poolItems, bool shortInfo = false ) {
315             for ( const PoolItem & pi : poolItems ) {
316               yOut << YAML::Value << YAML::BeginMap;
317
318               std::stringstream status;
319               status << pi.status();
320
321               yOut << YAML::Key << "job"     << YAML::Value << jobName
322                    << YAML::Key << "kind"    << YAML::Value << pi.kind().asString()
323                    << YAML::Key << "name"    << YAML::Value << pi.name()
324                    << YAML::Key << "status"  << YAML::Value << status.str();
325               if ( !shortInfo ) {
326                 yOut << YAML::Key << "channel" << YAML::Value << pi.repoInfo().alias()
327                      << YAML::Key << "arch"    << YAML::Value << pi.arch().asString()
328                      << YAML::Key << "version" << YAML::Value << pi.edition().version()
329                      << YAML::Key << "release" << YAML::Value << pi.edition().release();
330               }
331               yOut << YAML::EndMap;
332             }
333           };
334         };
335
336         const auto &writeMapJob = []( YAML::Emitter &yOut, const std::string &name, const std::map<std::string, std::string> &values = std::map<std::string, std::string>() ){
337           yOut << YAML::Value << YAML::BeginMap;
338           yOut << YAML::Key << "job"     << YAML::Value << name;
339           for ( const auto &v : values )
340             yOut << YAML::Key << v.first << YAML::Value << v.second;
341           yOut << YAML::EndMap;
342         };
343
344         writePoolItemJobs("install")( yOut, items_to_install );
345         writePoolItemJobs("keep")( yOut, items_keep );
346         writePoolItemJobs("uninstall")( yOut, items_to_remove, true );
347
348         if ( items_locked.size() )
349           writeJobsToFile("zypp-locks.yaml", items_locked, writePoolItemJobs("lock") );
350
351         for ( const auto &v : resolver.extraRequires() )
352           writeMapJob( yOut, "addRequire", { { "name", v.asString() } } );
353         for ( const auto &v : SystemCheck::instance().requiredSystemCap() )
354           writeMapJob( yOut, "addRequire", { { "name", v.asString() } } );
355
356         for ( const auto &v : resolver.extraConflicts() )
357           writeMapJob( yOut, "addConflict", { { "name", v.asString() } } );
358         for ( const auto &v : SystemCheck::instance().conflictSystemCap() )
359           writeMapJob( yOut, "addConflict", { { "name", v.asString() } } );
360
361         for ( const auto &v : resolver.upgradeRepos() )
362           writeMapJob( yOut, "upgradeRepo", { { "name", v.alias() } } );
363
364         if ( resolver.isUpgradeMode() )
365           writeMapJob( yOut, "distupgrade" );
366
367         if ( resolver.isUpdateMode() )
368           writeMapJob( yOut, "update" );
369
370         if ( resolver.isVerifyingMode() )
371           writeMapJob( yOut, "verify" );
372
373         yOut << YAML::EndSeq;
374         yOut << YAML::EndMap; // trial
375         yOut << YAML::EndSeq; // trials list
376         yOut << YAML::EndMap; // trials
377         yOut << YAML::EndMap; // root
378
379         std::ofstream fout( dumpPath+"/zypp-control.yaml" );
380         fout << yOut.c_str();
381
382         MIL << "createTestcase done at " << dumpPath << endl;
383         return true;
384       }
385       ///////////////////////////////////////////////////////////////////
386     };// namespace detail
387     /////////////////////////////////////////////////////////////////////
388     /////////////////////////////////////////////////////////////////////
389   };// namespace solver
390   ///////////////////////////////////////////////////////////////////////
391   ///////////////////////////////////////////////////////////////////////
392 };// namespace zypp
393 /////////////////////////////////////////////////////////////////////////