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