00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <fstream>
00014 #include <sstream>
00015 #include <streambuf>
00016
00017 #include "zypp/solver/detail/Testcase.h"
00018 #include "zypp/base/Logger.h"
00019 #include "zypp/base/LogControl.h"
00020 #include "zypp/base/GzStream.h"
00021 #include "zypp/base/String.h"
00022 #include "zypp/base/PtrTypes.h"
00023 #include "zypp/base/NonCopyable.h"
00024 #include "zypp/base/ReferenceCounted.h"
00025
00026 #include "zypp/parser/xml/XmlEscape.h"
00027
00028 #include "zypp/ZConfig.h"
00029 #include "zypp/PathInfo.h"
00030 #include "zypp/ResPool.h"
00031 #include "zypp/Repository.h"
00032
00033 #include "zypp/sat/detail/PoolImpl.h"
00034 #include "zypp/solver/detail/SystemCheck.h"
00035
00037 namespace zypp
00038 {
00039
00040 namespace solver
00041 {
00042
00043 namespace detail
00044 {
00045
00046 #define TAB "\t"
00047 #define TAB2 "\t\t"
00048
00049 using namespace std;
00050 using namespace zypp::str;
00051
00052
00053
00054 inline std::string xml_escape( const std::string &text )
00055 {
00056 return zypp::xml::escape(text);
00057 }
00058
00059 inline std::string xml_tag_enclose( const std::string &text, const std::string &tag, bool escape = false )
00060 {
00061 string result;
00062 result += "<" + tag + ">";
00063
00064 if ( escape)
00065 result += xml_escape(text);
00066 else
00067 result += text;
00068
00069 result += "</" + tag + ">";
00070 return result;
00071 }
00072
00073 template<class T>
00074 std::string helixXML( const T &obj );
00075
00076 template<>
00077 std::string helixXML( const Edition &edition )
00078 {
00079 stringstream str;
00080 str << xml_tag_enclose(edition.version(), "version");
00081 if (!edition.release().empty())
00082 str << xml_tag_enclose(edition.release(), "release");
00083 if (edition.epoch() != Edition::noepoch)
00084 str << xml_tag_enclose(numstring(edition.epoch()), "epoch");
00085 return str.str();
00086 }
00087
00088 template<>
00089 std::string helixXML( const Arch &arch )
00090 {
00091 stringstream str;
00092 str << xml_tag_enclose(arch.asString(), "arch");
00093 return str.str();
00094 }
00095
00096 template<>
00097 std::string helixXML( const Capability &cap )
00098 {
00099 stringstream str;
00100 CapDetail detail = cap.detail();
00101 if (detail.isSimple()) {
00102 if (detail.isVersioned()) {
00103 str << "<dep name='" << xml_escape(detail.name().asString()) << "'"
00104 << " op='" << xml_escape(detail.op().asString()) << "'"
00105 << " version='" << xml_escape(detail.ed().version()) << "'";
00106 if (!detail.ed().release().empty())
00107 str << " release='" << xml_escape(detail.ed().release()) << "'";
00108 if (detail.ed().epoch() != Edition::noepoch)
00109 str << " epoch='" << xml_escape(numstring(detail.ed().epoch())) << "'";
00110 str << " />" << endl;
00111 } else {
00112 str << "<dep name='" << xml_escape(cap.asString()) << "' />" << endl;
00113 }
00114 } else if (detail.isExpression()) {
00115 if (detail.capRel() == CapDetail::CAP_AND
00116 && detail.lhs().detail().isNamed()
00117 && detail.rhs().detail().isNamed()) {
00118
00119 str << "<dep name='packageand("
00120 << IdString(detail.lhs().id()) << ":"
00121 << IdString(detail.rhs().id()) << ")' />" << endl;
00122 } else {
00123
00124 IdString packageName;
00125 if (detail.capRel() == CapDetail::CAP_AND) {
00126 packageName = IdString(detail.lhs().id());
00127 detail = detail.rhs().detail();
00128 }
00129 if (detail.capRel() == CapDetail::CAP_NAMESPACE
00130 && detail.lhs().id() == NAMESPACE_MODALIAS) {
00131 str << "<dep name='modalias(";
00132 if (!packageName.empty())
00133 str << packageName << ":";
00134 str << IdString(detail.rhs().id()) << ")' />" << endl;
00135 } else {
00136 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
00137 }
00138 }
00139 } else {
00140 str << "<!--- ignoring '" << xml_escape(cap.asString()) << "' -->" << endl;
00141 }
00142
00143 return str.str();
00144 }
00145
00146 template<>
00147 std::string helixXML( const Capabilities &caps )
00148 {
00149 stringstream str;
00150 Capabilities::const_iterator it = caps.begin();
00151 str << endl;
00152 for ( ; it != caps.end(); ++it)
00153 {
00154 str << TAB2 << helixXML((*it));
00155 }
00156 str << TAB;
00157 return str.str();
00158 }
00159
00160 template<>
00161 std::string helixXML( const CapabilitySet &caps )
00162 {
00163 stringstream str;
00164 CapabilitySet::const_iterator it = caps.begin();
00165 str << endl;
00166 for ( ; it != caps.end(); ++it)
00167 {
00168 str << TAB2 << helixXML((*it));
00169 }
00170 str << TAB;
00171 return str.str();
00172 }
00173
00174 inline string helixXML( const Resolvable::constPtr &obj, Dep deptag_r )
00175 {
00176 stringstream out;
00177 Capabilities caps( obj->dep(deptag_r) );
00178 if ( ! caps.empty() )
00179 out << TAB << xml_tag_enclose(helixXML(caps), deptag_r.asString()) << endl;
00180 return out.str();
00181 }
00182
00183 std::string helixXML( const PoolItem &item )
00184 {
00185 const Resolvable::constPtr resolvable = item.resolvable();
00186 stringstream str;
00187 str << "<" << toLower (resolvable->kind().asString()) << ">" << endl;
00188 str << TAB << xml_tag_enclose (resolvable->name(), "name", true) << endl;
00189 str << TAB << xml_tag_enclose (item->vendor(), "vendor", true) << endl;
00190 str << TAB << xml_tag_enclose (item->buildtime().asSeconds(), "buildtime", true) << endl;
00191 if ( isKind<Package>(resolvable) ) {
00192 str << TAB << "<history>" << endl << TAB << "<update>" << endl;
00193 str << TAB2 << helixXML (resolvable->arch()) << endl;
00194 str << TAB2 << helixXML (resolvable->edition()) << endl;
00195 str << TAB << "</update>" << endl << TAB << "</history>" << endl;
00196 } else {
00197 str << TAB << helixXML (resolvable->arch()) << endl;
00198 str << TAB << helixXML (resolvable->edition()) << endl;
00199 }
00200 str << helixXML( resolvable, Dep::PROVIDES);
00201 str << helixXML( resolvable, Dep::PREREQUIRES);
00202 str << helixXML( resolvable, Dep::CONFLICTS);
00203 str << helixXML( resolvable, Dep::OBSOLETES);
00204 str << helixXML( resolvable, Dep::REQUIRES);
00205 str << helixXML( resolvable, Dep::RECOMMENDS);
00206 str << helixXML( resolvable, Dep::ENHANCES);
00207 str << helixXML( resolvable, Dep::SUPPLEMENTS);
00208 str << helixXML( resolvable, Dep::SUGGESTS);
00209
00210 str << "</" << toLower (resolvable->kind().asString()) << ">" << endl;
00211 return str.str();
00212 }
00213
00215
00216
00221 class HelixResolvable : public base::ReferenceCounted, private base::NonCopyable{
00222
00223 private:
00224 std::string dumpFile;
00225 ofgzstream *file;
00226
00227 public:
00228 HelixResolvable (const std::string & path);
00229 ~HelixResolvable ();
00230
00231 void addResolvable (const PoolItem item)
00232 { *file << helixXML (item); }
00233
00234 std::string filename ()
00235 { return dumpFile; }
00236 };
00237
00238 DEFINE_PTR_TYPE(HelixResolvable);
00239 IMPL_PTR_TYPE(HelixResolvable);
00240
00241 typedef std::map<Repository, HelixResolvable_Ptr> RepositoryTable;
00242
00243 HelixResolvable::HelixResolvable(const std::string & path)
00244 :dumpFile (path)
00245 {
00246 file = new ofgzstream(path.c_str());
00247 if (!file) {
00248 ZYPP_THROW (Exception( "Can't open " + path ) );
00249 }
00250
00251 *file << "<channel><subchannel>" << endl;
00252 }
00253
00254 HelixResolvable::~HelixResolvable()
00255 {
00256 *file << "</subchannel></channel>" << endl;
00257 delete(file);
00258 }
00259
00261
00262
00267 class HelixControl {
00268
00269 private:
00270 std::string dumpFile;
00271 std::ofstream *file;
00272
00273 public:
00274 HelixControl (const std::string & controlPath,
00275 const RepositoryTable & sourceTable,
00276 const Arch & systemArchitecture,
00277 const LocaleSet &languages,
00278 const std::string & systemPath = "solver-system.xml.gz",
00279 const bool forceResolve = false,
00280 const bool onlyRequires = false,
00281 const bool ignorealreadyrecommended = false);
00282 HelixControl ();
00283 ~HelixControl ();
00284
00285 void installResolvable (const ResObject::constPtr &resObject,
00286 const ResStatus &status);
00287 void lockResolvable (const ResObject::constPtr &resObject,
00288 const ResStatus &status);
00289 void keepResolvable (const ResObject::constPtr &resObject,
00290 const ResStatus &status);
00291 void deleteResolvable (const ResObject::constPtr &resObject,
00292 const ResStatus &status);
00293 void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
00294 void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
00295
00296 void distupgrade ();
00297 void verifySystem ();
00298 void update ();
00299
00300 std::string filename () { return dumpFile; }
00301 };
00302
00303 HelixControl::HelixControl(const std::string & controlPath,
00304 const RepositoryTable & repoTable,
00305 const Arch & systemArchitecture,
00306 const LocaleSet &languages,
00307 const std::string & systemPath,
00308 const bool forceResolve,
00309 const bool onlyRequires,
00310 const bool ignorealreadyrecommended)
00311 :dumpFile (controlPath)
00312 {
00313 file = new ofstream(controlPath.c_str());
00314 if (!file) {
00315 ZYPP_THROW (Exception( "Can't open " + controlPath ) );
00316 }
00317
00318 *file << "<?xml version=\"1.0\"?>" << endl
00319 << "<!-- testcase generated by YaST -->" << endl
00320 << "<test>" << endl
00321 << "<setup arch=\"" << systemArchitecture << "\">" << endl
00322 << TAB << "<system file=\"" << systemPath << "\"/>" << endl << endl;
00323 for ( RepositoryTable::const_iterator it = repoTable.begin();
00324 it != repoTable.end(); ++it ) {
00325 RepoInfo repo = it->first.info();
00326 *file << TAB << "<!-- " << endl
00327 << TAB << "- alias : " << repo.alias() << endl;
00328 for ( RepoInfo::urls_const_iterator itUrl = repo.baseUrlsBegin();
00329 itUrl != repo.baseUrlsEnd();
00330 ++itUrl )
00331 {
00332 *file << TAB << "- url : " << *itUrl << endl;
00333 }
00334 *file << TAB << "- path : " << repo.path() << endl;
00335 *file << TAB << "- type : " << repo.type() << endl;
00336 *file << TAB << "- generated : " << (it->first.generatedTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
00337 *file << TAB << "- outdated : " << (it->first.suggestedExpirationTimestamp()).form( "%Y-%m-%d %H:%M:%S" ) << endl;
00338 *file << TAB << " -->" << endl;
00339
00340 *file << TAB << "<channel file=\"" << str::numstring((long)it->first.id())
00341 << "-package.xml.gz\" name=\"" << repo.alias() << "\""
00342 << " priority=\"" << repo.priority()
00343 << "\" />" << endl << endl;
00344 }
00345 for (LocaleSet::const_iterator iter = languages.begin(); iter != languages.end(); iter++) {
00346 *file << TAB << "<locale name=\"" << iter->code()
00347 << "\" />" << endl;
00348 }
00349
00350 if (forceResolve)
00351 *file << TAB << "<forceResolve/>" << endl;
00352 if (onlyRequires)
00353 *file << TAB << "<onlyRequires/>" << endl;
00354
00355 if (ignorealreadyrecommended)
00356 *file << TAB << "<ignorealreadyrecommended/>" << endl;
00357
00358 *file << "</setup>" << endl
00359 << "<trial>" << endl;
00360 }
00361
00362 HelixControl::HelixControl()
00363 :dumpFile ("/var/log/YaST2/solverTestcase/solver-test.xml")
00364 {
00365 HelixControl (dumpFile);
00366 }
00367
00368 HelixControl::~HelixControl()
00369 {
00370 *file << "</trial>" << endl
00371 << "</test>" << endl;
00372 delete(file);
00373 }
00374
00375 void HelixControl::installResolvable(const ResObject::constPtr &resObject,
00376 const ResStatus &status)
00377 {
00378 *file << "<install channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00379 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00380 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00381 << " status=\"" << status << "\""
00382 << "/>" << endl;
00383 }
00384
00385 void HelixControl::lockResolvable(const ResObject::constPtr &resObject,
00386 const ResStatus &status)
00387 {
00388 *file << "<lock channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00389 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00390 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00391 << " status=\"" << status << "\""
00392 << "/>" << endl;
00393 }
00394
00395 void HelixControl::keepResolvable(const ResObject::constPtr &resObject,
00396 const ResStatus &status)
00397 {
00398 *file << "<keep channel=\"" << resObject->repoInfo().alias() << "\" kind=\"" << toLower (resObject->kind().asString()) << "\""
00399 << " name=\"" << resObject->name() << "\"" << " arch=\"" << resObject->arch().asString() << "\""
00400 << " version=\"" << resObject->edition().version() << "\"" << " release=\"" << resObject->edition().release() << "\""
00401 << " status=\"" << status << "\""
00402 << "/>" << endl;
00403 }
00404
00405 void HelixControl::deleteResolvable(const ResObject::constPtr &resObject,
00406 const ResStatus &status)
00407 {
00408 *file << "<uninstall " << " kind=\"" << toLower (resObject->kind().asString()) << "\""
00409 << " name=\"" << resObject->name() << "\""
00410 << " status=\"" << status << "\""
00411 << "/>" << endl;
00412 }
00413
00414 void HelixControl::addDependencies (const CapabilitySet & capRequire, const CapabilitySet & capConflict)
00415 {
00416 for (CapabilitySet::const_iterator iter = capRequire.begin(); iter != capRequire.end(); iter++) {
00417 *file << "<addRequire " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
00418 }
00419 for (CapabilitySet::const_iterator iter = capConflict.begin(); iter != capConflict.end(); iter++) {
00420 *file << "<addConflict " << " name=\"" << iter->asString() << "\"" << "/>" << endl;
00421 }
00422 }
00423
00424 void HelixControl::addUpgradeRepos( const std::set<Repository> & upgradeRepos_r )
00425 {
00426 for_( it, upgradeRepos_r.begin(), upgradeRepos_r.end() )
00427 {
00428 *file << "<upgradeRepo name=\"" << it->alias() << "\"/>" << endl;
00429 }
00430 }
00431
00432 void HelixControl::distupgrade()
00433 {
00434 *file << "<distupgrade/>" << endl;
00435 }
00436
00437 void HelixControl::verifySystem()
00438 {
00439 *file << "<verify/>" << endl;
00440 }
00441
00442 void HelixControl::update()
00443 {
00444 *file << "<update/>" << endl;
00445 }
00446
00447
00448
00449 Testcase::Testcase()
00450 :dumpPath("/var/log/YaST2/solverTestcase")
00451 {}
00452
00453 Testcase::Testcase(const std::string & path)
00454 :dumpPath(path)
00455 {}
00456
00457 Testcase::~Testcase()
00458 {}
00459
00460 bool Testcase::createTestcase(Resolver & resolver, bool dumpPool, bool runSolver)
00461 {
00462 PathInfo path (dumpPath);
00463
00464 if ( !path.isExist() ) {
00465 if (zypp::filesystem::assert_dir (dumpPath)!=0) {
00466 ERR << "Cannot create directory " << dumpPath << endl;
00467 return false;
00468 }
00469 } else {
00470 if (!path.isDir()) {
00471 ERR << dumpPath << " is not a directory." << endl;
00472 return false;
00473 }
00474
00475 if (dumpPool)
00476 zypp::filesystem::clean_dir (dumpPath);
00477 }
00478
00479 if (runSolver) {
00480 zypp::base::LogControl::TmpLineWriter tempRedirect;
00481 zypp::base::LogControl::instance().logfile( dumpPath +"/y2log" );
00482 zypp::base::LogControl::TmpExcessive excessive;
00483
00484 resolver.resolvePool();
00485 }
00486
00487 ResPool pool = resolver.pool();
00488 RepositoryTable repoTable;
00489 PoolItemList items_to_install;
00490 PoolItemList items_to_remove;
00491 PoolItemList items_locked;
00492 PoolItemList items_keep;
00493 HelixResolvable_Ptr system = NULL;
00494
00495 if (dumpPool)
00496 system = new HelixResolvable(dumpPath + "/solver-system.xml.gz");
00497
00498 for ( ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it )
00499 {
00500 Resolvable::constPtr res = it->resolvable();
00501
00502 if ( system && it->status().isInstalled() ) {
00503
00504 system->addResolvable (*it);
00505 } else {
00506
00507 Repository repo = it->resolvable()->satSolvable().repository();
00508 if (dumpPool) {
00509 if (repoTable.find (repo) == repoTable.end()) {
00510 repoTable[repo] = new HelixResolvable(dumpPath + "/"
00511 + str::numstring((long)repo.id())
00512 + "-package.xml.gz");
00513 }
00514 repoTable[repo]->addResolvable (*it);
00515 }
00516 }
00517
00518 if ( it->status().isToBeInstalled()
00519 && !(it->status().isBySolver())) {
00520 items_to_install.push_back (*it);
00521 }
00522 if ( it->status().isKept()
00523 && !(it->status().isBySolver())) {
00524 items_keep.push_back (*it);
00525 }
00526 if ( it->status().isToBeUninstalled()
00527 && !(it->status().isBySolver())) {
00528 items_to_remove.push_back (*it);
00529 }
00530 if ( it->status().isLocked()
00531 && !(it->status().isBySolver())) {
00532 items_locked.push_back (*it);
00533 }
00534 }
00535
00536
00537 HelixControl control (dumpPath + "/solver-test.xml",
00538 repoTable,
00539 ZConfig::instance().systemArchitecture(),
00540 pool.getRequestedLocales(),
00541 "solver-system.xml.gz",
00542 resolver.forceResolve(),
00543 resolver.onlyRequires(),
00544 resolver.ignoreAlreadyRecommended() );
00545
00546 for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
00547 control.installResolvable (iter->resolvable(), iter->status());
00548 }
00549
00550 for (PoolItemList::const_iterator iter = items_locked.begin(); iter != items_locked.end(); iter++) {
00551 control.lockResolvable (iter->resolvable(), iter->status());
00552 }
00553
00554 for (PoolItemList::const_iterator iter = items_keep.begin(); iter != items_keep.end(); iter++) {
00555 control.keepResolvable (iter->resolvable(), iter->status());
00556 }
00557
00558 for (PoolItemList::const_iterator iter = items_to_remove.begin(); iter != items_to_remove.end(); iter++) {
00559 control.deleteResolvable (iter->resolvable(), iter->status());
00560 }
00561
00562 control.addDependencies (resolver.extraRequires(), resolver.extraConflicts());
00563 control.addDependencies (SystemCheck::instance().requiredSystemCap(),
00564 SystemCheck::instance().conflictSystemCap());
00565 control.addUpgradeRepos( resolver.upgradeRepos() );
00566
00567 if (resolver.isUpgradeMode())
00568 control.distupgrade ();
00569 if (resolver.isUpdateMode())
00570 control.update();
00571 if (resolver.isVerifyingMode())
00572 control.verifySystem();
00573
00574 return true;
00575 }
00576
00577
00579 };
00582 };
00585 };