Imported Upstream version 15.11.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 PoolItem & obj, Dep deptag_r )
183 {
184   stringstream out;
185   Capabilities caps( obj[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   stringstream str;
194   str << "<" << item.kind() << ">" << endl;
195   str << TAB << xml_tag_enclose( item.name(), "name", true ) << endl;
196   str << TAB << xml_tag_enclose( item.vendor().asString(), "vendor", true ) << endl;
197   str << TAB << xml_tag_enclose( item.buildtime().asSeconds(), "buildtime", true ) << endl;
198   if ( isKind<Package>( item ) ) {
199       str << TAB << "<history>" << endl << TAB << "<update>" << endl;
200       str << TAB2 << helixXML( item.arch() ) << endl;
201       str << TAB2 << helixXML( item.edition() ) << endl;
202       str << TAB << "</update>" << endl << TAB << "</history>" << endl;
203   } else {
204       str << TAB << helixXML( item.arch() ) << endl;
205       str << TAB << helixXML( item.edition() ) << endl;
206   }
207   str << helixXML( item, Dep::PROVIDES );
208   str << helixXML( item, Dep::PREREQUIRES );
209   str << helixXML( item, Dep::CONFLICTS );
210   str << helixXML( item, Dep::OBSOLETES );
211   str << helixXML( item, Dep::REQUIRES );
212   str << helixXML( item, Dep::RECOMMENDS );
213   str << helixXML( item, Dep::ENHANCES );
214   str << helixXML( item, Dep::SUPPLEMENTS );
215   str << helixXML( item, Dep::SUGGESTS );
216
217   str << "</" << item.kind() << ">" << endl;
218   return str.str();
219 }
220
221 ///////////////////////////////////////////////////////////////////
222 //
223 //      CLASS NAME : HelixResolvable
224 /**
225  * Creates a file in helix format which includes all available
226  * or installed packages,patches,selections.....
227  **/
228 class  HelixResolvable : public base::ReferenceCounted, private base::NonCopyable{
229
230   private:
231     std::string dumpFile; // Path of the generated testcase
232     ofgzstream *file;
233
234   public:
235     HelixResolvable (const std::string & path);
236     ~HelixResolvable ();
237
238     void addResolvable (const PoolItem item)
239     { *file << helixXML (item); }
240
241     std::string filename ()
242     { return dumpFile; }
243 };
244
245 DEFINE_PTR_TYPE(HelixResolvable);
246 IMPL_PTR_TYPE(HelixResolvable);
247
248 typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
249
250 HelixResolvable::HelixResolvable(const std::string & path)
251     :dumpFile (path)
252 {
253     file = new ofgzstream(path.c_str());
254     if (!file) {
255         ZYPP_THROW (Exception( "Can't open " + path ) );
256     }
257
258     *file << "<channel><subchannel>" << endl;
259 }
260
261 HelixResolvable::~HelixResolvable()
262 {
263     *file << "</subchannel></channel>" << endl;
264     delete(file);
265 }
266
267 ///////////////////////////////////////////////////////////////////
268 //
269 //      CLASS NAME : HelixControl
270 /**
271  * Creates a file in helix format which contains all controll
272  * action of a testcase ( file is known as *-test.xml)
273  **/
274 class  HelixControl {
275
276   private:
277     std::string dumpFile; // Path of the generated testcase
278     std::ofstream *file;
279
280   public:
281     HelixControl (const std::string & controlPath,
282                   const RepositoryTable & sourceTable,
283                   const Arch & systemArchitecture,
284                   const target::Modalias::ModaliasList & modaliasList,
285                   const std::set<std::string> & multiversionSpec,
286                   const std::string & systemPath,
287                   const bool forceResolve,
288                   const bool onlyRequires,
289                   const bool ignorealreadyrecommended);
290     HelixControl ();
291     ~HelixControl ();
292
293     void installResolvable( const PoolItem & pi_r );
294     void lockResolvable( const PoolItem & pi_r );
295     void keepResolvable( const PoolItem & pi_r );
296     void deleteResolvable( const PoolItem & pi_r );
297     void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
298     void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
299
300     void distupgrade ();
301     void verifySystem ();
302     void update ();
303
304     std::string filename () { return dumpFile; }
305 };
306
307 HelixControl::HelixControl(const std::string & controlPath,
308                            const RepositoryTable & repoTable,
309                            const Arch & systemArchitecture,
310                            const target::Modalias::ModaliasList & modaliasList,
311                            const std::set<std::string> & multiversionSpec,
312                            const std::string & systemPath,
313                            const bool forceResolve,
314                            const bool onlyRequires,
315                            const bool ignorealreadyrecommended)
316     :dumpFile (controlPath)
317 {
318     file = new ofstream(controlPath.c_str());
319     if (!file) {
320         ZYPP_THROW (Exception( "Can't open " + controlPath ) );
321     }
322
323     *file << "<?xml version=\"1.0\"?>" << endl
324           << "<!-- testcase generated by YaST -->" << endl
325           << "<test>" << endl
326           << "<setup arch=\"" << systemArchitecture << "\">" << endl
327           << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
328     for ( RepositoryTable::const_iterator it = repoTable.begin();
329           it != repoTable.end(); ++it ) {
330         RepoInfo repo = it->first.info();
331         *file << TAB << "<!-- " << endl
332               << TAB << "- alias       : " << repo.alias() << endl;
333         for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
334               itUrl != repo.baseUrlsEnd();
335               ++itUrl )
336         {
337             *file << TAB << "- url         : " << *itUrl << endl;
338         }
339         *file << TAB << "- path        : " << repo.path() << endl;
340         *file << TAB << "- type        : " << repo.type() << endl;
341         *file << TAB << "- generated   : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
342         *file << TAB << "- outdated    : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
343         *file << TAB << " -->" << endl;
344
345         *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
346               << "-package.xml.gz\" name=\"" << repo.alias() << "\""
347               << " priority=\"" << repo.priority()
348               << "\" />" << endl << endl;
349     }
350
351     // HACK: directly access sat::pool
352     const sat::Pool & satpool( sat::Pool::instance() );
353
354     // RequestedLocales
355     const LocaleSet & addedLocales( satpool.getAddedRequestedLocales() );
356     const LocaleSet & removedLocales( satpool.getRemovedRequestedLocales() );
357     const LocaleSet & requestedLocales( satpool.getRequestedLocales() );
358
359     for ( Locale l : requestedLocales )
360     {
361       const char * fate = ( addedLocales.count(l) ? "\" fate=\"added" : "" );
362       *file << TAB << "<locale name=\"" << l << fate << "\" />" << endl;
363     }
364     for ( Locale l : removedLocales )
365     {
366       *file << TAB << "<locale name=\"" << l << "\" fate=\"removed\" />" << endl;
367     }
368
369     // AutoInstalled
370     for ( IdString::IdType n : satpool.autoInstalled() )
371     {
372       *file << TAB << "<autoinst name=\"" << IdString(n) << "\" />" << endl;
373     }
374
375
376
377     for_( it, modaliasList.begin(), modaliasList.end() ) {
378         *file << TAB << "<modalias name=\"" <<  *it
379               << "\" />" << endl;
380     }
381
382     for_( it, multiversionSpec.begin(), multiversionSpec.end() ) {
383         *file << TAB << "<multiversion name=\"" <<  *it
384               << "\" />" << endl;
385     }
386
387     if (forceResolve)
388         *file << TAB << "<forceResolve/>" << endl;
389     if (onlyRequires)
390         *file << TAB << "<onlyRequires/>" << endl;
391     if (ignorealreadyrecommended)
392         *file << TAB << "<ignorealreadyrecommended/>" << endl;
393
394     *file << "</setup>" << endl
395           << "<trial>" << endl;
396 }
397
398 HelixControl::HelixControl()
399     :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
400 {
401     HelixControl (dumpFile);
402 }
403
404 HelixControl::~HelixControl()
405 {
406     *file << "</trial>" << endl
407           << "</test>" << endl;
408     delete(file);
409 }
410
411 void HelixControl::installResolvable( const PoolItem & pi_r )
412 {
413     *file << "<install channel=\"" << pi_r.repoInfo().alias() << "\""
414           << " kind=\"" << pi_r.kind() << "\""
415           << " name=\"" << pi_r.name() << "\""
416           << " arch=\"" << pi_r.arch() << "\""
417           << " version=\"" << pi_r.edition().version() << "\""
418           << " release=\"" << pi_r.edition().release() << "\""
419           << " status=\"" << pi_r.status() << "\""
420           << "/>" << endl;
421 }
422
423 void HelixControl::lockResolvable( const PoolItem & pi_r )
424 {
425     *file << "<lock channel=\"" << pi_r.repoInfo().alias() << "\""
426           << " kind=\"" << pi_r.kind() << "\""
427           << " name=\"" << pi_r.name() << "\""
428           << " arch=\"" << pi_r.arch() << "\""
429           << " version=\"" << pi_r.edition().version() << "\""
430           << " release=\"" << pi_r.edition().release() << "\""
431           << " status=\"" << pi_r.status() << "\""
432           << "/>" << endl;
433 }
434
435 void HelixControl::keepResolvable( const PoolItem & pi_r )
436 {
437     *file << "<keep channel=\"" << pi_r.repoInfo().alias() << "\""
438           << " kind=\"" << pi_r.kind() << "\""
439           << " name=\"" << pi_r.name() << "\""
440           << " arch=\"" << pi_r.arch() << "\""
441           << " version=\"" << pi_r.edition().version() << "\""
442           << " release=\"" << pi_r.edition().release() << "\""
443           << " status=\"" << pi_r.status() << "\""
444           << "/>" << endl;
445 }
446
447 void HelixControl::deleteResolvable( const PoolItem & pi_r )
448 {
449     *file << "<uninstall  kind=\"" << pi_r.kind() << "\""
450           << " name=\"" << pi_r.name() << "\""
451           << " status=\"" << pi_r.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 ( const PoolItem & pi : pool )
540     {
541         if ( system && pi.status().isInstalled() ) {
542             // system channel
543             system->addResolvable( pi );
544         } else {
545             // repo channels
546             Repository repo  = pi.repository();
547             if (dumpPool) {
548                 if (repoTable.find (repo) == repoTable.end()) {
549                     repoTable[repo] = new HelixResolvable(dumpPath + "/"
550                                                           + str::numstring((long)repo.id())
551                                                           + "-package.xml.gz");
552                 }
553                 repoTable[repo]->addResolvable( pi );
554             }
555         }
556
557         if ( pi.status().isToBeInstalled()
558              && !(pi.status().isBySolver())) {
559             items_to_install.push_back( pi );
560         }
561         if ( pi.status().isKept()
562              && !(pi.status().isBySolver())) {
563             items_keep.push_back( pi );
564         }
565         if ( pi.status().isToBeUninstalled()
566              && !(pi.status().isBySolver())) {
567             items_to_remove.push_back( pi );
568         }
569         if ( pi.status().isLocked()
570              && !(pi.status().isBySolver())) {
571             items_locked.push_back( pi );
572         }
573     }
574
575     // writing control file "*-test.xml"
576     HelixControl control (dumpPath + "/solver-test.xml",
577                           repoTable,
578                           ZConfig::instance().systemArchitecture(),
579                           target::Modalias::instance().modaliasList(),
580                           ZConfig::instance().multiversionSpec(),
581                           "solver-system.xml.gz",
582                           resolver.forceResolve(),
583                           resolver.onlyRequires(),
584                           resolver.ignoreAlreadyRecommended() );
585
586     for ( const PoolItem & pi : items_to_install )
587     { control.installResolvable( pi ); }
588
589     for ( const PoolItem & pi : items_locked )
590     { control.lockResolvable( pi ); }
591
592     for ( const PoolItem & pi : items_keep )
593     { control.keepResolvable( pi ); }
594
595     for ( const PoolItem & pi : items_to_remove )
596     { control.deleteResolvable( pi ); }
597
598     control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
599     control.addDependencies (SystemCheck::instance().requiredSystemCap(),
600                              SystemCheck::instance().conflictSystemCap());
601     control.addUpgradeRepos( resolver.upgradeRepos() );
602
603     if (resolver.isUpgradeMode())
604         control.distupgrade ();
605     if (resolver.isUpdateMode())
606         control.update();
607     if (resolver.isVerifyingMode())
608         control.verifySystem();
609
610     return true;
611 }
612
613
614       ///////////////////////////////////////////////////////////////////
615     };// namespace detail
616     /////////////////////////////////////////////////////////////////////
617     /////////////////////////////////////////////////////////////////////
618   };// namespace solver
619   ///////////////////////////////////////////////////////////////////////
620   ///////////////////////////////////////////////////////////////////////
621 };// namespace zypp
622 /////////////////////////////////////////////////////////////////////////