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