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