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