Imported Upstream version 15.10.0
[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
17 #define ZYPP_USE_RESOLVER_INTERNALS
18
19 #include "zypp/solver/detail/Testcase.h"
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/LogControl.h"
22 #include "zypp/base/GzStream.h"
23 #include "zypp/base/String.h"
24 #include "zypp/base/PtrTypes.h"
25 #include "zypp/base/NonCopyable.h"
26 #include "zypp/base/ReferenceCounted.h"
27
28 #include "zypp/parser/xml/XmlEscape.h"
29
30 #include "zypp/ZConfig.h"
31 #include "zypp/PathInfo.h"
32 #include "zypp/ResPool.h"
33 #include "zypp/Repository.h"
34 #include "zypp/target/modalias/Modalias.h"
35
36 #include "zypp/sat/detail/PoolImpl.h"
37 #include "zypp/solver/detail/Resolver.h"
38 #include "zypp/solver/detail/SystemCheck.h"
39
40 /////////////////////////////////////////////////////////////////////////
41 namespace zypp
42 { ///////////////////////////////////////////////////////////////////////
43   ///////////////////////////////////////////////////////////////////////
44   namespace solver
45   { /////////////////////////////////////////////////////////////////////
46     /////////////////////////////////////////////////////////////////////
47     namespace detail
48     { ///////////////////////////////////////////////////////////////////
49
50 #define TAB "\t"
51 #define TAB2 "\t\t"
52
53 using namespace std;
54 using namespace zypp::str;
55
56 //---------------------------------------------------------------------------
57
58 inline std::string xml_escape( const std::string &text )
59 {
60   return zypp::xml::escape(text);
61 }
62
63 inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
64 {
65   string result;
66   result += "<" + tag + ">";
67
68   if ( escape)
69    result += xml_escape(text);
70   else
71    result += text;
72
73   result += "</" + tag + ">";
74   return result;
75 }
76
77 template<class T>
78 std::string helixXML( const T &obj ); //undefined
79
80 template<>
81 std::string helixXML( const Edition &edition )
82 {
83     stringstream str;
84     str << xml_tag_enclose(edition.version(), "version");
85     if (!edition.release().empty())
86         str << xml_tag_enclose(edition.release(), "release");
87     if (edition.epoch() != Edition::noepoch)
88         str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
89     return str.str();
90 }
91
92 template<>
93 std::string helixXML( const Arch &arch )
94 {
95     stringstream str;
96     str << xml_tag_enclose(arch.asString(), "arch");
97     return str.str();
98 }
99
100 template<>
101 std::string helixXML( const Capability &cap )
102 {
103     stringstream str;
104     CapDetail detail = cap.detail();
105     if (detail.isSimple()) {
106         if (detail.isVersioned()) {
107             str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
108                 << " op='" << xml_escape(detail.op().asString()) << "'"
109                 << " version='" <<  xml_escape(detail.ed().version()) << "'";
110             if (!detail.ed().release().empty())
111                 str << " release='" << xml_escape(detail.ed().release()) << "'";
112             if (detail.ed().epoch() != Edition::noepoch)
113                 str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
114             str << " />" << endl;
115         } else {
116             str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
117         }
118     } else if (detail.isExpression()) {
119         if (detail.capRel() == CapDetail::CAP_AND
120             && detail.lhs().detail().isNamed()
121             && detail.rhs().detail().isNamed()) {
122             // packageand dependency
123             str << "<dep name='packageand("
124                 << IdString(detail.lhs().id()) << ":"
125                 << IdString(detail.rhs().id()) << ")' />" << endl;
126         } else if (detail.capRel() == CapDetail::CAP_NAMESPACE
127             && detail.lhs().id() == NAMESPACE_OTHERPROVIDERS) {
128             str << "<dep name='otherproviders("
129                 << IdString(detail.rhs().id()) << ")' />" << endl;
130         } else {
131             // modalias ?
132             IdString packageName;
133             if (detail.capRel() == CapDetail::CAP_AND) {
134                 packageName = IdString(detail.lhs().id());
135                 detail = detail.rhs().detail();
136             }
137             if (detail.capRel() == CapDetail::CAP_NAMESPACE
138                 && detail.lhs().id() == NAMESPACE_MODALIAS) {
139                 str << "<dep name='modalias(";
140                 if (!packageName.empty())
141                     str << packageName << ":";
142                 str << IdString(detail.rhs().id()) << ")' />" << endl;
143             } else {
144                 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
145             }
146         }
147     } else {
148         str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
149     }
150
151     return str.str();
152 }
153
154 template<>
155 std::string helixXML( const Capabilities &caps )
156 {
157     stringstream str;
158     Capabilities::const_iterator it = caps.begin();
159     str << endl;
160     for ( ; it != caps.end(); ++it)
161     {
162         str << TAB2 << helixXML((*it));
163     }
164     str << TAB;
165     return str.str();
166 }
167
168 template<>
169 std::string helixXML( const CapabilitySet &caps )
170 {
171     stringstream str;
172     CapabilitySet::const_iterator it = caps.begin();
173     str << endl;
174     for ( ; it != caps.end(); ++it)
175     {
176         str << TAB2 << helixXML((*it));
177     }
178     str << TAB;
179     return str.str();
180 }
181
182 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
183 {
184   stringstream out;
185   Capabilities caps( obj->dep(deptag_r) );
186   if ( ! caps.empty() )
187     out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
188   return out.str();
189 }
190
191 std::string helixXML( const PoolItem &item )
192 {
193   const Resolvable::constPtr resolvable = item.resolvable();
194   stringstream str;
195   str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
196   str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
197   str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
198   str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;
199   if ( isKind<Package>(resolvable) ) {
200       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
201       str << TAB2 << helixXML (resolvable->arch()) << endl;
202       str << TAB2 << helixXML (resolvable->edition()) << endl;
203       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
204   } else {
205       str << TAB << helixXML (resolvable->arch()) << endl;
206       str << TAB << helixXML (resolvable->edition()) << endl;
207   }
208   str << helixXML( resolvable, Dep::PROVIDES);
209   str << helixXML( resolvable, Dep::PREREQUIRES);
210   str << helixXML( resolvable, Dep::CONFLICTS);
211   str << helixXML( resolvable, Dep::OBSOLETES);
212   str << helixXML( resolvable, Dep::REQUIRES);
213   str << helixXML( resolvable, Dep::RECOMMENDS);
214   str << helixXML( resolvable, Dep::ENHANCES);
215   str << helixXML( resolvable, Dep::SUPPLEMENTS);
216   str << helixXML( resolvable, Dep::SUGGESTS);
217
218   str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
219   return str.str();
220 }
221
222 ///////////////////////////////////////////////////////////////////
223 //
224 //      CLASS NAME : HelixResolvable
225 /**
226  * Creates a file in helix format which includes all available
227  * or installed packages,patches,selections.....
228  **/
229 class  HelixResolvable : public base::ReferenceCounted, private base::NonCopyable{
230
231   private:
232     std::string dumpFile; // Path of the generated testcase
233     ofgzstream *file;
234
235   public:
236     HelixResolvable (const std::string & path);
237     ~HelixResolvable ();
238
239     void addResolvable (const PoolItem item)
240     { *file << helixXML (item); }
241
242     std::string filename ()
243     { return dumpFile; }
244 };
245
246 DEFINE_PTR_TYPE(HelixResolvable);
247 IMPL_PTR_TYPE(HelixResolvable);
248
249 typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
250
251 HelixResolvable::HelixResolvable(const std::string & path)
252     :dumpFile (path)
253 {
254     file = new ofgzstream(path.c_str());
255     if (!file) {
256         ZYPP_THROW (Exception( "Can't open " + path ) );
257     }
258
259     *file << "<channel><subchannel>" << endl;
260 }
261
262 HelixResolvable::~HelixResolvable()
263 {
264     *file << "</subchannel></channel>" << endl;
265     delete(file);
266 }
267
268 ///////////////////////////////////////////////////////////////////
269 //
270 //      CLASS NAME : HelixControl
271 /**
272  * Creates a file in helix format which contains all controll
273  * action of a testcase ( file is known as *-test.xml)
274  **/
275 class  HelixControl {
276
277   private:
278     std::string dumpFile; // Path of the generated testcase
279     std::ofstream *file;
280
281   public:
282     HelixControl (const std::string & controlPath,
283                   const RepositoryTable & sourceTable,
284                   const Arch & systemArchitecture,
285                   const target::Modalias::ModaliasList & modaliasList,
286                   const std::set<std::string> & multiversionSpec,
287                   const std::string & systemPath,
288                   const bool forceResolve,
289                   const bool onlyRequires,
290                   const bool ignorealreadyrecommended);
291     HelixControl ();
292     ~HelixControl ();
293
294     void installResolvable (const ResObject::constPtr &resObject,
295                             const ResStatus &status);
296     void lockResolvable (const ResObject::constPtr &resObject,
297                          const ResStatus &status);
298     void keepResolvable (const ResObject::constPtr &resObject,
299                          const ResStatus &status);
300     void deleteResolvable (const ResObject::constPtr &resObject,
301                            const ResStatus &status);
302     void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
303     void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
304
305     void distupgrade ();
306     void verifySystem ();
307     void update ();
308
309     std::string filename () { return dumpFile; }
310 };
311
312 HelixControl::HelixControl(const std::string & controlPath,
313                            const RepositoryTable & repoTable,
314                            const Arch & systemArchitecture,
315                            const target::Modalias::ModaliasList & modaliasList,
316                            const std::set<std::string> & multiversionSpec,
317                            const std::string & systemPath,
318                            const bool forceResolve,
319                            const bool onlyRequires,
320                            const bool ignorealreadyrecommended)
321     :dumpFile (controlPath)
322 {
323     file = new ofstream(controlPath.c_str());
324     if (!file) {
325         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
326     }
327
328     *file << "<?xml version=\"1.0\"?>" << endl
329           << "<!-- testcase generated by YaST -->" << endl
330           << "<test>" << endl
331           << "<setup arch=\"" << systemArchitecture << "\">" << endl
332           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
333     for ( RepositoryTable::const_iterator it = repoTable.begin();
334           it != repoTable.end(); ++it ) {
335         RepoInfo repo = it->first.info();
336         *file << TAB << "<!-- " << endl
337               << TAB << "- alias       : " << repo.alias() << endl;
338         for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
339               itUrl != repo.baseUrlsEnd();
340               ++itUrl )
341         {
342             *file << TAB << "- url         : " << *itUrl << endl;
343         }
344         *file << TAB << "- path        : " << repo.path() << endl;
345         *file << TAB << "- type        : " << repo.type() << endl;
346         *file << TAB << "- generated   : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
347         *file << TAB << "- outdated    : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
348         *file << TAB << " -->" << endl;
349
350         *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
351               << "-package.xml.gz\" name=\"" << repo.alias() << "\""
352               << " priority=\"" << repo.priority()
353               << "\" />" << endl << endl;
354     }
355
356     // HACK: directly access sat::pool
357     const sat::Pool & satpool( sat::Pool::instance() );
358
359     // RequestedLocales
360     const LocaleSet & addedLocales( satpool.getAddedRequestedLocales() );
361     const LocaleSet & removedLocales( satpool.getRemovedRequestedLocales() );
362     const LocaleSet & requestedLocales( satpool.getRequestedLocales() );
363
364     for ( Locale l : requestedLocales )
365     {
366       const char * fate = ( addedLocales.count(l) ? "\" fate=\"added" : "" );
367       *file << TAB << "<locale name=\"" << l << fate << "\" />" << endl;
368     }
369     for ( Locale l : removedLocales )
370     {
371       *file << TAB << "<locale name=\"" << l << "\" fate=\"removed\" />" << endl;
372     }
373
374     // AutoInstalled
375     for ( IdString::IdType n : satpool.autoInstalled() )
376     {
377       *file << TAB << "<autoinst name=\"" << IdString(n) << "\" />" << endl;
378     }
379
380
381
382     for_( it, modaliasList.begin(), modaliasList.end() ) {
383         *file << TAB << "<modalias name=\"" <<  *it
384               << "\" />" << endl;
385     }
386
387     for_( it, multiversionSpec.begin(), multiversionSpec.end() ) {
388         *file << TAB << "<multiversion name=\"" <<  *it
389               << "\" />" << endl;
390     }
391
392     if (forceResolve)
393         *file << TAB << "<forceResolve/>" << endl;
394     if (onlyRequires)
395         *file << TAB << "<onlyRequires/>" << endl;
396     if (ignorealreadyrecommended)
397         *file << TAB << "<ignorealreadyrecommended/>" << endl;
398
399     *file << "</setup>" << endl
400           << "<trial>" << endl;
401 }
402
403 HelixControl::HelixControl()
404     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
405 {
406     HelixControl (dumpFile);
407 }
408
409 HelixControl::~HelixControl()
410 {
411     *file << "</trial>" << endl
412           << "</test>" << endl;
413     delete(file);
414 }
415
416 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
417                                      const ResStatus &status)
418 {
419     *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
420           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
421           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
422           << " status=\"" << status << "\""
423           << "/>" << endl;
424 }
425
426 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
427                                   const ResStatus &status)
428 {
429     *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
430           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
431           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
432           << " status=\"" << status << "\""
433           << "/>" << endl;
434 }
435
436 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
437                                   const ResStatus &status)
438 {
439     *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
440           << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
441           << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
442           << " status=\"" << status << "\""
443           << "/>" << endl;
444 }
445
446 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
447                                     const ResStatus &status)
448 {
449     *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
450           << " name=\"" << resObject->name() << "\""
451           << " status=\"" << status << "\""
452           << "/>" << endl;
453 }
454
455 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
456 {
457     for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
458         *file << "<addRequire " <<  " name=\"" << iter->asString() << "\"" << "/>" << endl;
459     }
460     for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
461         *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
462     }
463 }
464
465 void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
466 {
467   for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
468   {
469     *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
470   }
471 }
472
473 void HelixControl::distupgrade()
474 {
475     *file << "<distupgrade/>" << endl;
476 }
477
478 void HelixControl::verifySystem()
479 {
480     *file << "<verify/>" << endl;
481 }
482
483 void HelixControl::update()
484 {
485     *file << "<update/>" << endl;
486 }
487
488 //---------------------------------------------------------------------------
489
490 Testcase::Testcase()
491     :dumpPath("/var/log/YaST2/solverTestcase")
492 {}
493
494 Testcase::Testcase(const std::string & path)
495     :dumpPath(path)
496 {}
497
498 Testcase::~Testcase()
499 {}
500
501 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
502 {
503     PathInfo path (dumpPath);
504
505     if ( !path.isExist() ) {
506         if (zypp::filesystem::assert_dir (dumpPath)!=0) {
507             ERR << "Cannot create directory " << dumpPath << endl;
508             return false;
509         }
510     } else {
511         if (!path.isDir()) {
512             ERR << dumpPath << " is not a directory." << endl;
513             return false;
514         }
515         // remove old stuff if pool will be dump
516         if (dumpPool)
517             zypp::filesystem::clean_dir (dumpPath);
518     }
519
520     if (runSolver) {
521         zypp::base::LogControl::TmpLineWriter tempRedirect;
522         zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
523         zypp::base::LogControl::TmpExcessive excessive;
524
525         resolver.resolvePool();
526     }
527
528     ResPool pool        = resolver.pool();
529     RepositoryTable     repoTable;
530     PoolItemList        items_to_install;
531     PoolItemList        items_to_remove;
532     PoolItemList        items_locked;
533     PoolItemList        items_keep;
534     HelixResolvable_Ptr system = NULL;
535
536     if (dumpPool)
537         system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
538
539     for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
540     {
541         Resolvable::constPtr res = it->resolvable();
542
543         if ( system && it->status().isInstalled() ) {
544             // system channel
545             system->addResolvable (*it);
546         } else {
547             // repo channels
548             Repository repo  = it->resolvable()->satSolvable().repository();
549             if (dumpPool) {
550                 if (repoTable.find (repo) == repoTable.end()) {
551                     repoTable[repo] = new HelixResolvable(dumpPath + "/"
552                                                           + str::numstring((long)repo.id())
553                                                           + "-package.xml.gz");
554                 }
555                 repoTable[repo]->addResolvable (*it);
556             }
557         }
558
559         if ( it->status().isToBeInstalled()
560              && !(it->status().isBySolver())) {
561             items_to_install.push_back (*it);
562         }
563         if ( it->status().isKept()
564              && !(it->status().isBySolver())) {
565             items_keep.push_back (*it);
566         }
567         if ( it->status().isToBeUninstalled()
568              && !(it->status().isBySolver())) {
569             items_to_remove.push_back (*it);
570         }
571         if ( it->status().isLocked()
572              && !(it->status().isBySolver())) {
573             items_locked.push_back (*it);
574         }
575     }
576
577     // writing control file "*-test.xml"
578     HelixControl control (dumpPath + "/solver-test.xml",
579                           repoTable,
580                           ZConfig::instance().systemArchitecture(),
581                           target::Modalias::instance().modaliasList(),
582                           ZConfig::instance().multiversionSpec(),
583                           "solver-system.xml.gz",
584                           resolver.forceResolve(),
585                           resolver.onlyRequires(),
586                           resolver.ignoreAlreadyRecommended() );
587
588     for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
589         control.installResolvable (iter->resolvable(), iter->status());
590     }
591
592     for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
593         control.lockResolvable (iter->resolvable(), iter->status());
594     }
595
596     for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
597         control.keepResolvable (iter->resolvable(), iter->status());
598     }
599
600     for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
601         control.deleteResolvable (iter->resolvable(), iter->status());
602     }
603
604     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
605     control.addDependencies (SystemCheck::instance().requiredSystemCap(),
606                              SystemCheck::instance().conflictSystemCap());
607     control.addUpgradeRepos( resolver.upgradeRepos() );
608
609     if (resolver.isUpgradeMode())
610         control.distupgrade ();
611     if (resolver.isUpdateMode())
612         control.update();
613     if (resolver.isVerifyingMode())
614         control.verifySystem();
615
616     return true;
617 }
618
619
620       ///////////////////////////////////////////////////////////////////
621     };// namespace detail
622     /////////////////////////////////////////////////////////////////////
623     /////////////////////////////////////////////////////////////////////
624   };// namespace solver
625   ///////////////////////////////////////////////////////////////////////
626   ///////////////////////////////////////////////////////////////////////
627 };// namespace zypp
628 /////////////////////////////////////////////////////////////////////////