diff --git a/code/ryzom/common/src/game_share/deployment_configuration.cpp b/code/ryzom/common/src/game_share/deployment_configuration.cpp new file mode 100644 index 000000000..b5f0b94e9 --- /dev/null +++ b/code/ryzom/common/src/game_share/deployment_configuration.cpp @@ -0,0 +1,1507 @@ +/** \file deployment_configuration.cpp + * + */ + +//----------------------------------------------------------------------------- +// include +//----------------------------------------------------------------------------- + +// pre compiled headers +#include "stdpch.h" + +// nel +#include "nel/misc/types_nl.h" +#include "nel/misc/common.h" +#include "nel/misc/time_nl.h" +#include "nel/misc/smart_ptr.h" +#include "nel/misc/singleton.h" +#include "nel/misc/command.h" +#include "nel/misc/file.h" + +// game share +#include "utils.h" +#include "deployment_configuration.h" + + +//----------------------------------------------------------------------------- +// namespaces +//----------------------------------------------------------------------------- + +using namespace std; +using namespace NLMISC; + + +//----------------------------------------------------------------------------- +// namespace DEPCFG +//----------------------------------------------------------------------------- + +namespace DEPCFG +{ + //----------------------------------------------------------------------------- + // forward class declarations + //----------------------------------------------------------------------------- + + struct SExeRecord; + class CInfoBlock; + class CInfoContainer; + class CDeploymentConfigurationImplementation; + + + //----------------------------------------------------------------------------- + // struct SExeRecord + //----------------------------------------------------------------------------- + + struct SExeRecord + { + typedef std::set TDataEntries; + typedef std::vector TCfgEntries; + + NLMISC::CSString FullName; + + NLMISC::CSString DomainName; + NLMISC::CSString ShardName; + NLMISC::CSString UniqueName; + NLMISC::CSString CmdLine; + NLMISC::CSString Host; + NLMISC::CSString StartOrder; + TDataEntries DataEntries; + TCfgEntries CfgEntries; + TCfgEntries CfgEntriesPost; + + // serial method + void serial(NLMISC::IStream& stream) + { + stream.serial(FullName); + stream.serial(DomainName); + stream.serial(ShardName); + stream.serial(UniqueName); + stream.serial(CmdLine); + stream.serial(Host); + stream.serialCont(DataEntries); + stream.serialCont(CfgEntries); + stream.serialCont(CfgEntriesPost); + } + }; + + + //----------------------------------------------------------------------------- + // class CInfoBlock + //----------------------------------------------------------------------------- + + class CInfoBlock: public NLMISC::CRefCount + { + public: + // ctors + CInfoBlock(const NLMISC::CSString& name=""); + + // write accessors + void setDomainName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void setShardName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void setUniqueName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void setCmdLine(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void setHost(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void setStartOrder(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + + void addUseEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void addDataEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + + void addCfgEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + void addCfgEntryPost(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors); + + void addCfgFile(const NLMISC::CSString& fileName,const NLMISC::CSString& context,uint32& errors); + void addCfgFilePost(const NLMISC::CSString& fileName,const NLMISC::CSString& context,uint32& errors); + + // direct read accessors + uint32 getNumParents() const; + const NLMISC::CSString& getName() const; + const NLMISC::CSString& getDomainName() const; + + // indirect read accessors + bool isDomain() const; + bool isShard() const; + + // setup the _Children vector from the _UseEntries vector + void setupChildren(CInfoContainer* container,uint32& errors); + + // traverse the tree of children, cumulating data on exes, and instantiating the exe records in 'container' + void buildExeSet(CInfoContainer* container,uint32& errors,const SExeRecord& parentExeRecord=SExeRecord()); + + // serial + void serial(NLMISC::IStream& stream); + + // display the contents of the container + void dump(NLMISC::CLog& log) const; + + private: + // data types + typedef std::set TUseEntries; + typedef std::set TDataEntries; + typedef std::vector TCfgEntries; + + typedef std::vector TChildren; + + // private methods + bool _haveCircularRef(CInfoBlock* other) const; + + // data (basics) + NLMISC::CSString _Name; + uint32 _NumParents; + TChildren _Children; + + // data extracted from input file + NLMISC::CSString _DomainName; + NLMISC::CSString _ShardName; + NLMISC::CSString _UniqueName; + NLMISC::CSString _CmdLine; + NLMISC::CSString _Host; + NLMISC::CSString _StartOrder; + TUseEntries _UseEntries; + TDataEntries _DataEntries; + TCfgEntries _CfgEntries; + TCfgEntries _CfgEntriesPost; + }; + + + //----------------------------------------------------------------------------- + // class CInfoContainer + //----------------------------------------------------------------------------- + + class CInfoContainer + { + public: + // public interface + void clear(); + bool empty() const; + + bool read(const NLMISC::CSString& fileName); + void serial(NLMISC::IStream& stream); + + void getHostNames(THostNames& result) const; + void getDomainNames(TDomainNames& result) const; + void getShardNames(const TDomainName& domainName,TShardNames& result) const; + void getAppNames(const THostName& hostName,const TDomainName& domainName,TAppNames& result) const; + void getAppNames(const THostName& hostName,const TDomainName& domainName,const TShardName& shardName,TAppNames& result) const; + + void getHost(const THostName& hostName,SHostDescription& result) const; + void getDomain(const TDomainName& domainName, SDomainDescription& result) const; + void getShard(const TDomainName& domainName,const TShardName& shardName,SShardDescription& result) const; + void getApp(const TDomainName& domainName,const TAppName& appName,SAppDescription& result) const; + + void dumpInfoBlocks(NLMISC::CLog& log) const; + void dumpDomains(NLMISC::CLog& log) const; + + // interface used by CInfoBlock methods + CInfoBlock* getInfoBlock(const NLMISC::CSString& name); + void addExe(const SExeRecord& exeRecord,uint32& errors); + + private: + // data types + typedef NLMISC::CSmartPtr TInfoBlockPtr; + typedef std::map< NLMISC::CSString,TInfoBlockPtr > TInfoBlocks; + typedef std::set< NLMISC::CSString > TFileNameSet; + typedef std::vector< SExeRecord > TExeRecords; + typedef std::vector TExeIdx; + typedef std::map TShardExes; + typedef std::map TDomainExes; + + // private methods + void _readFile(const NLMISC::CSString& fileName,uint32& errors,TFileNameSet& fileNameStack); + void _buildDomainTree(uint32& errors); + void _buildExeSet(uint32& errors); + + // data + TInfoBlockPtr _CurrentInfoBlock; + TInfoBlocks _InfoBlocks; + TExeRecords _ExeRecords; + TDomainExes _DomainExes; + }; + + + //----------------------------------------------------------------------------- + // class CDeploymentConfigurationImplementation + //----------------------------------------------------------------------------- + + class CDeploymentConfigurationImplementation: public CSingleton, public CDeploymentConfiguration + { + public: + bool read(const NLMISC::CSString& fileName); + void write(const NLMISC::CSString& fileName); + void serial(NLMISC::IStream& stream); + + void getHostNames(THostNames& result) const; + void getDomainNames(TDomainNames& result) const; + void getShardNames(const TDomainName& domainName,TShardNames& result) const; + void getAppNames(const THostName& hostName,const TDomainName& domainName,TAppNames& result) const; + void getAppNames(const THostName& hostName,const TDomainName& domainName,const TShardName& shardName,TAppNames& result) const; + + void getHost(const THostName& hostName,SHostDescription& result) const; + void getDomain(const TDomainName& domainName, SDomainDescription& result) const; + void getShard(const TDomainName& domainName,const TShardName& shardName,SShardDescription& result) const; + void getApp(const TDomainName& domainName,const TAppName& appName,SAppDescription& result) const; + + void dumpInfoBlocks(NLMISC::CLog& log) const; + void dumpDomains(NLMISC::CLog& log) const; + + private: + CInfoContainer _InfoContainer; + }; + + + //----------------------------------------------------------------------------- + // methods CInfoBlock + //----------------------------------------------------------------------------- + + CInfoBlock::CInfoBlock(const NLMISC::CSString& name) + { + _Name= name; + _NumParents= 0; + } + + void CInfoBlock::setDomainName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_DomainName.empty(),context+"Attempting to set a domain name more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty domain name",++errors; return); + _DomainName=entry; + } + + void CInfoBlock::setShardName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_ShardName.empty(),context+"Attempting to set a shard name more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty shard name",++errors; return); + _ShardName=entry; + } + + void CInfoBlock::setUniqueName(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_UniqueName.empty(),context+"Attempting to set a name more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty name",++errors; return); + _UniqueName=entry; + } + + void CInfoBlock::setCmdLine(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_CmdLine.empty(),context+"Attempting to set a cmdLine more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty cmdLine",++errors; return); + _CmdLine=entry; + } + + void CInfoBlock::setHost(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_Host.empty(),context+"Attempting to set a host more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty host",++errors; return); + _Host=entry; + } + + void CInfoBlock::setStartOrder(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(!_StartOrder.empty(),context+"Attempting to set a startOrder more than once for the same info block",++errors; return); + DROP_IF(entry.empty(),context+"Attempting to set an empty startOrder",++errors; return); + _StartOrder=entry; + } + + void CInfoBlock::addUseEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(_UseEntries.find(entry)!=_UseEntries.end(),context+"Ignoring duplicate refference to 'use' clause: "+entry,return); + _UseEntries.insert(entry); + } + + void CInfoBlock::addDataEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + DROP_IF(_DataEntries.find(entry)!=_DataEntries.end(),context+"Ignoring duplicate refference to 'data' clause: "+entry,return); + _DataEntries.insert(entry); + } + + void CInfoBlock::addCfgEntry(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + _CfgEntries.push_back(entry); + } + + void CInfoBlock::addCfgEntryPost(const NLMISC::CSString& entry,const NLMISC::CSString& context,uint32& errors) + { + _CfgEntriesPost.push_back(entry); + } + + void CInfoBlock::addCfgFile(const NLMISC::CSString& fileName,const NLMISC::CSString& context,uint32& errors) + { + // make sure a file name is supplied + DROP_IF(fileName.empty(),context+"No file name found following 'cfgFile'", ++errors;return); + + // read in the src file + NLMISC::CSString fileContents; + fileContents.readFromFile(fileName); + DROP_IF(fileContents.empty(),"File not found: "+fileName, ++errors;return); + + // split the file contents into lines + NLMISC::CVectorSString lines; + fileContents.splitLines(lines); + + // append the lines to the '_CfgEntries' container + _CfgEntries.insert(_CfgEntries.end(),lines.begin(),lines.end()); + } + + void CInfoBlock::addCfgFilePost(const NLMISC::CSString& fileName,const NLMISC::CSString& context,uint32& errors) + { + // make sure a file name is supplied + DROP_IF(fileName.empty(),context+"No file name found following 'cfgFilePost'", ++errors;return); + + // read in the src file + NLMISC::CSString fileContents; + fileContents.readFromFile(fileName); + DROP_IF(fileContents.empty(),"File not found: "+fileName, ++errors;return); + + // split the file contents into lines + NLMISC::CVectorSString lines; + fileContents.splitLines(lines); + + // prepend the lines to the '_CfgEntriesPost' container + _CfgEntriesPost.insert(_CfgEntriesPost.begin(),lines.begin(),lines.end()); + } + + uint32 CInfoBlock::getNumParents() const + { + return _NumParents; + } + + const NLMISC::CSString& CInfoBlock::getName() const + { + return _Name; + } + + const NLMISC::CSString& CInfoBlock::getDomainName() const + { + return _DomainName; + } + + bool CInfoBlock::isDomain() const + { + return !_DomainName.empty(); + } + + bool CInfoBlock::isShard() const + { + return !_ShardName.empty(); + } + + void CInfoBlock::setupChildren(CInfoContainer* container,uint32& errors) + { + // start by clearing out the child vector that we're going to fill + _Children.clear(); + + // iterate over the the 'use' clauses + for (TUseEntries::iterator it= _UseEntries.begin(); it!= _UseEntries.end(); ++it) + { + const NLMISC::CSString& theEntry= *it; + + // try to get a pointer to the refferenced info block... + CInfoBlock* infoBlockPtr= container->getInfoBlock(theEntry); + DROP_IF(infoBlockPtr==NULL,"Failed to find block named '"+theEntry+"' while fixing up children of block: "+_Name, ++errors;continue); + + // make sure that this block doesn't figure amongst the children of the refferenced info block (to avoid circular refs) + DROP_IF(_haveCircularRef(infoBlockPtr),"Circular dependency found between definitions of '"+_Name+"' and '"+theEntry+"'", ++errors;continue); + + // add the info block to our children + _Children.push_back(infoBlockPtr); + ++(infoBlockPtr->_NumParents); + } + } + + void CInfoBlock::buildExeSet(CInfoContainer* container,uint32& errors,const SExeRecord& parentExeRecord) + { + // setup a record to accumulate data into as we traverse the tree, starting + // with a copy of the data passed in from parents + SExeRecord theExe= parentExeRecord; + + // add a chunk to the exe record's 'FullName' property + theExe.FullName+= (theExe.FullName.empty()?"":".")+ _Name; + + // make sure we don't have any field duplication... + DROP_IF(!_DomainName.empty() && !theExe.DomainName.empty(), "more than one domain found in: "+theExe.FullName, ++errors ); + DROP_IF(!_ShardName.empty() && !theExe.ShardName.empty(), "more than one shard found in: "+theExe.FullName, ++errors ); + DROP_IF(!_CmdLine.empty() && !theExe.CmdLine.empty(), "more than one cmdLine found in: "+theExe.FullName, ++errors ); + DROP_IF(!_Host.empty() && !theExe.Host.empty(), "more than one host found in: "+theExe.FullName, ++errors ); + WARN_IF(!_UniqueName.empty() && !theExe.UniqueName.empty(), "replacing name '"+theExe.UniqueName+"' with '"+_UniqueName+"' in: "+theExe.FullName); + + // fill our own data into the exe record + if (!_DomainName.empty()) theExe.DomainName = _DomainName; + if (!_ShardName.empty()) theExe.ShardName = _ShardName; + if (!_UniqueName.empty()) theExe.UniqueName = _UniqueName; + if (!_CmdLine.empty()) theExe.CmdLine = _CmdLine; + if (!_Host.empty()) theExe.Host = _Host; + if (!_StartOrder.empty()) theExe.StartOrder = _StartOrder; + // merge contents of 2 sets + theExe.DataEntries.insert( _DataEntries.begin(), _DataEntries.end() ); + // append or pre-pend contents of one vector to another + theExe.CfgEntries.insert( theExe.CfgEntries.end(), _CfgEntries.begin(), _CfgEntries.end() ); + theExe.CfgEntriesPost.insert( theExe.CfgEntriesPost.begin(), _CfgEntriesPost.begin(), _CfgEntriesPost.end() ); + + // if this is the node with the cmdLine then think about updating the unique name... + if (!_CmdLine.empty() && theExe.UniqueName.empty()) + { + theExe.UniqueName= _Name; + } + + // do something with the exe record depending on whetherwe're a tree branch or leaf + if (_Children.empty()) + { + // merge the cfg entries together to make a single block + theExe.CfgEntries.insert(theExe.CfgEntries.end(),theExe.CfgEntriesPost.begin(),theExe.CfgEntriesPost.end()); + theExe.CfgEntriesPost.clear(); + + // this is a leaf node (it has no children) so it must describe an executable + container->addExe(theExe,errors); + } + else + { + // this is a branch node so recurse into children + for (TChildren::iterator it=_Children.begin();it!=_Children.end();++it) + { + (*it)->buildExeSet(container,errors,theExe); + } + } + } + + void CInfoBlock::serial(NLMISC::IStream& stream) + { + if (stream.isReading()) + { + // if we're reading then we clear out the children vector - it'll be rebuilt at the end of the serial + _Children.clear(); + } + + stream.serial(_Name); + stream.serial(_NumParents); + stream.serial(_DomainName); + stream.serial(_ShardName); + stream.serial(_UniqueName); + stream.serial(_CmdLine); + stream.serial(_Host); + + stream.serialCont(_UseEntries); + stream.serialCont(_DataEntries); + stream.serialCont(_CfgEntries); + stream.serialCont(_CfgEntriesPost); + } + + void CInfoBlock::dump(NLMISC::CLog& log) const + { + //***************************** + // NOTE: + // This method is called to + // create a text save of the + // data that we contain - it + // must output ALL data that + // read() requires and in a + // read()-compatible format + //***************************** + + log.displayNL("define %s // refferenced by %u other defines",_Name.c_str(),_NumParents); + + if (!_DomainName.empty()) + { + log.displayNL("\tdomain\t%s",_DomainName.c_str()); + } + + if (!_ShardName.empty()) + { + log.displayNL("\tshard\t%s",_ShardName.c_str()); + } + + if (!_UniqueName.empty()) + { + log.displayNL("\tname\t%s",_UniqueName.c_str()); + } + + if (!_CmdLine.empty()) + { + log.displayNL("\tcmdLine\t%s",_CmdLine.c_str()); + } + + if (!_Host.empty()) + { + log.displayNL("\thost\t%s",_Host.c_str()); + } + + for (TUseEntries::const_iterator it=_UseEntries.begin(); it!=_UseEntries.end(); ++it) + { + log.displayNL("\tuse\t%s",it->c_str()); + } + + for (TDataEntries::const_iterator it=_DataEntries.begin(); it!=_DataEntries.end(); ++it) + { + log.displayNL("\tdata\t%s",it->c_str()); + } + + for (TCfgEntries::const_iterator it=_CfgEntries.begin(); it!=_CfgEntries.end(); ++it) + { + log.displayNL("\tcfg\t%s",it->c_str()); + } + + for (TCfgEntries::const_iterator it=_CfgEntriesPost.begin(); it!=_CfgEntriesPost.end(); ++it) + { + log.displayNL("\tcfgAfter\t%s",it->c_str()); + } + + log.displayNL(""); + } + + bool CInfoBlock::_haveCircularRef(CInfoBlock* other) const + { + // in the case of a circular refference we end up with the 'other'=='this' + if (this==other) + return true; + + // recurse into children looking for a deep circular ref + for (TChildren::const_iterator it= other->_Children.begin();it!=other->_Children.end();++it) + { + CInfoBlock* child= *it; + if (_haveCircularRef(child)) + return true; + } + + // no circular ref found so return false + return false; + } + + //----------------------------------------------------------------------------- + // methods CInfoContainer + //----------------------------------------------------------------------------- + + void CInfoContainer::clear() + { + _InfoBlocks.clear(); + _CurrentInfoBlock= NULL; + _ExeRecords.clear(); + _DomainExes.clear(); + } + + bool CInfoContainer::empty() const + { + return _InfoBlocks.empty(); + } + + bool CInfoContainer::read(const NLMISC::CSString& fileName) + { + // start by clearing out our contents... + clear(); + + // setup some basics + uint32 errors= 0; + TFileNameSet fileNameSet; + + // read in the src file + _readFile(fileName,errors,fileNameSet); + + // build the blocks into a tree + _buildDomainTree(errors); + + // build the set of executables from the tree + _buildExeSet(errors); + + // make sure that no errors were encountered... + DROP_IF(errors!=0,NLMISC::toString("%s: Parse Failed: %u errors found",fileName.c_str(),errors),clear(); return false); + + return true; + } + + void CInfoContainer::_readFile(const NLMISC::CSString& fileName,uint32& errors,TFileNameSet& fileNameSet) + { + // read in the src file + NLMISC::CSString fileContents; + fileContents.readFromFile(fileName); + DROP_IF(fileContents.empty(),"File not found: "+fileName, ++errors;return); + + // split the file into lines + NLMISC::CVectorSString lines; + fileContents.splitLines(lines); + + // process the lines one by one + for (uint32 i=0;i' but found: "+line, ++errors;continue); + + if (keyword=="domain") { _CurrentInfoBlock->setDomainName(args,context,errors); } + else if (keyword=="shard") { _CurrentInfoBlock->setShardName(args,context,errors); } + else if (keyword=="name") { _CurrentInfoBlock->setUniqueName(args,context,errors); } + else if (keyword=="cmdLine") { _CurrentInfoBlock->setCmdLine(args,context,errors); } + else if (keyword=="host") { _CurrentInfoBlock->setHost(args,context,errors); } + else if (keyword=="startOrder") { _CurrentInfoBlock->setStartOrder(args,context,errors); } + else if (keyword=="use") { _CurrentInfoBlock->addUseEntry(args,context,errors); } + else if (keyword=="data") { _CurrentInfoBlock->addDataEntry(args,context,errors); } + else if (keyword=="cfg") { _CurrentInfoBlock->addCfgEntry(rawArgs,context,errors); } + else if (keyword=="cfgAfter") { _CurrentInfoBlock->addCfgEntryPost(rawArgs,context,errors); } + else if (keyword=="cfgFile") { _CurrentInfoBlock->addCfgFile(args,context,errors); } + else if (keyword=="cfgFileAfter") { _CurrentInfoBlock->addCfgFilePost(args,context,errors); } + else { DROP(context+"Unrecognised keyword: "+line, ++errors;continue); } + } + } + } + + void CInfoContainer::_buildDomainTree(uint32& errors) + { + // iterate over the info block container, setting up 'child' vectors + for (TInfoBlocks::iterator it= _InfoBlocks.begin(); it!= _InfoBlocks.end(); ++it) + { + CInfoBlock& theInfoBlock= *it->second; + theInfoBlock.setupChildren(this,errors); + } + + // display the list of orphans + for (TInfoBlocks::iterator it= _InfoBlocks.begin(); it!= _InfoBlocks.end(); ++it) + { + CInfoBlock& theInfoBlock= *it->second; + WARN_IF(!theInfoBlock.isDomain() && theInfoBlock.getNumParents()==0,"Found unrefferenced info block: "+theInfoBlock.getName()); + } + } + + void CInfoContainer::_buildExeSet(uint32& errors) + { + // iterate over the info block container, looking for domains + for (TInfoBlocks::iterator it= _InfoBlocks.begin(); it!= _InfoBlocks.end(); ++it) + { + CInfoBlock& theInfoBlock= *it->second; + if (theInfoBlock.isDomain()) + { + const NLMISC::CSString& domainName= theInfoBlock.getDomainName(); + DROP_IF(_DomainExes.find(domainName)!=_DomainExes.end(),"Duplicate domain name found: "+domainName,++errors;continue); + nldebug("Building executable set for domain: %s",domainName.c_str()); + theInfoBlock.buildExeSet(this,errors); + } + + // fixup the names in the domain to make them unique + + // run through the exes a first time to determine which unique names are unique and which are not + std::map nameCounts; + for (TExeRecords::iterator it2= _ExeRecords.begin(); it2!=_ExeRecords.end(); ++it2) + { + // skip anything that's not from our domain + if (it2->DomainName!=theInfoBlock.getDomainName()) + continue; + + // get hold of the name + NLMISC::CSString& name= it2->UniqueName; + // yell if the name already looks like a 'fixed up' name + DROP_IF(name.right(3).left(1)=="_" && (name.right(2)=="00" || name.right(2).atoui()!=0),"Appending '_' to name ending in '_00' style format as this can clash with auto renumbering => "+name+'_',name+='_') + // compose a second version of the name with the shard name added + NLMISC::CSString name_with_shard= name+'_'+it2->ShardName; + // insert both versions of the name into the unique name counter + ++nameCounts[name]; + ++nameCounts[name_with_shard]; + } + + // run through the exes a second time to fix names that are not unique + std::map nameIdx; + for (TExeRecords::iterator it2= _ExeRecords.begin(); it2!=_ExeRecords.end(); ++it2) + { + // skip anything that's not from our domain + if (it2->DomainName!=theInfoBlock.getDomainName()) + continue; + + // get hold of the name + NLMISC::CSString& name= it2->UniqueName; + // if the name is unique then continue + if (nameCounts[name]==1) + continue; + + // compose a second version of the name with the shard name added + name+='_'; + name+=it2->ShardName; + // if the name is unique within the shard then continue + if (nameCounts[name]==1) + continue; + + // make the name name unique by appending a number to it + uint32 idx= ++nameIdx[name]; + name+=NLMISC::toString("_%02u",idx); + } + } + } + + void CInfoContainer::serial(NLMISC::IStream& stream) + { + if (stream.isReading()) + { + // start by clearing out our contents... + clear(); + + // get the number of info blocks from the stream + uint32 count; + stream.serial(count); + + // get the info blocks from the stream one by one + for (uint32 i=0;igetName()]= _CurrentInfoBlock; + } + + // setup an error accumulator + uint32 errors= 0; + + // build the blocks into a tree + _buildDomainTree(errors); + + // build the set of executables from the tree + _buildExeSet(errors); + + // make sure that no errors were encountered... + DROP_IF(errors!=0,NLMISC::toString("Serial Failed: %u errors found",errors), clear();return); + + // note - on exit, _CurrentInfoBlock refferences the last info block read + } + else + { + // put the number of info blocks to the stream + uint32 count= _InfoBlocks.size(); + stream.serial(count); + // put the info blocks to the stream one by one + for (TInfoBlocks::iterator it= _InfoBlocks.begin(); it!=_InfoBlocks.end(); ++it) + { + stream.serial(*(it->second)); + } + } + } + + void CInfoContainer::getHostNames(THostNames& result) const + { + // clear out the result before we begin work... + result.clear(); + + // use a little set to avoid adding host names more than once + std::set namesFound; + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that are on hosts that we've already dealt with + if (namesFound.find(theApp.Host)!=namesFound.end()) continue; + + // we've found a new host so add it to our result container + result.push_back(theApp.Host); + namesFound.insert(theApp.Host); + + found=true; + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no hosts were identified with the given domain + // this is not an error case so we just add a debug for info + nldebug("getHostNames failed - no hosts found"); + } + } + + void CInfoContainer::getDomainNames(TDomainNames& result) const + { + // clear out the result before we begin work... + result.clear(); + + // fill the result in from our internal data + for (TDomainExes::const_iterator dit= _DomainExes.begin(); dit!=_DomainExes.end(); ++dit) + { + const NLMISC::CSString& domainName= dit->first; + result.push_back(domainName); + } + + if (result.empty()) + { + // as a note - if we get here it is because the request has failed - no domains were found + // this is not an error case so we just add a debug for info + nldebug("getDomainNames failed - no domains found"); + } + } + + void CInfoContainer::getShardNames(const TDomainName& domainName,TShardNames& result) const + { + // clear out the result before we begin work... + result.clear(); + + // fill the result in from our internal data + bool found= false; + for (TDomainExes::const_iterator dit= _DomainExes.begin(); dit!=_DomainExes.end(); ++dit) + { + // ignore shards that aren't in the requested domain + const NLMISC::CSString& domName= dit->first; + if (domName!=domainName) continue; + + // run throught he shards for our chosen domain... + const TShardExes& shards= dit->second; + for (TShardExes::const_iterator sit= shards.begin(); sit!=shards.end(); ++sit) + { + const NLMISC::CSString shardName= sit->first; + result.push_back(shardName); + } + + found=true; + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no shards were identified with the given domain + // this is not an error case so we just add a debug for info + nldebug("getShardNames failed for domainName('%s')",domainName.c_str()); + } + } + + void CInfoContainer::getAppNames(const THostName& hostName,const TDomainName& domainName,TAppNames& result) const + { + // clear out the result before we begin work... + result.clear(); + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that aren't in the requested domain + if (theApp.DomainName!=domainName) continue; + + // ignore exes that are on the wrong host + if (theApp.Host!=hostName) continue; + + // we've found a new host so add it to our result container + result.push_back(theApp.UniqueName); + + found=true; + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no apps were identified with the given host and domain + // this is not an error case so we just add a debug for info + nldebug("getAppNames failed for hostName('%s'), domainName('%s')",hostName.c_str(),domainName.c_str()); + } + } + + void CInfoContainer::getAppNames(const THostName& hostName,const TDomainName& domainName,const TShardName& shardName,TAppNames& result) const + { + // clear out the result before we begin work... + result.clear(); + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that aren't in the requested domain + if (theApp.DomainName!=domainName) continue; + + // ignore exes that are on the wrong host + if (theApp.Host!=hostName) continue; + + // ignore exes that are on the shard host + if (theApp.ShardName!=shardName) continue; + + // we've found a new host so add it to our result container + result.push_back(theApp.UniqueName); + + found=true; + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no apps were identified with the given host, shard and domain + // this is not an error case so we just add a debug for info + nldebug("getAppNames failed for hostName('%s'), domainName('%s'), shardName('%s')",hostName.c_str(),domainName.c_str(),shardName.c_str()); + } + } + + void CInfoContainer::getHost(const THostName& hostName,SHostDescription& result) const + { + // clear out the result before we begin work... + result.clear(); + result.HostName= hostName; + + // use a set to buildup lists of unique host names + typedef std::set TNameSet; + TNameSet domainNames; + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that aren't on the requested host + if (theApp.Host!=hostName) continue; + + // add this exe's domain to the domains set + domainNames.insert(theApp.DomainName); + + found=true; + } + + // copy the hosts set to the result record + for (TNameSet::const_iterator it= domainNames.begin(); it!=domainNames.end(); ++it) + { + result.Domains.push_back(*it); + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no host was identified with the given name + // this is not an error case so we just add a debug for info + nldebug("getHost failed for hostName('%s')",hostName.c_str()); + } + } + + void CInfoContainer::getDomain(const TDomainName& domainName, SDomainDescription& result) const + { + // clear out the result before we begin work... + result.clear(); + result.DomainName= domainName; + + // use a couple of sets to buildup lists of unique host and shard names + typedef std::set TNameSet; + TNameSet hostNames; + TNameSet shardNames; + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that aren't in the requested domain + if (theApp.DomainName!=domainName) continue; + + // add this exe's shard to the shards set + shardNames.insert(theApp.ShardName); + + // add this exe's host to the hosts set + hostNames.insert(theApp.Host); + + // add this exe's unique name to the result apps record + result.Apps.push_back(theApp.UniqueName); + + found=true; + } + + // copy the hosts set to the result record + for (TNameSet::const_iterator it= hostNames.begin(); it!=hostNames.end(); ++it) + { + result.Hosts.push_back(*it); + } + + // copy the shards set to the result record + for (TNameSet::const_iterator it= shardNames.begin(); it!=shardNames.end(); ++it) + { + result.Shards.push_back(*it); + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no domain was identified with the given name + // this is not an error case so we just add a debug for info + nldebug("getDomain failed for domainName('%s')",domainName.c_str()); + } + } + + void CInfoContainer::getShard(const TDomainName& domainName,const TShardName& shardName,SShardDescription& result) const + { + // clear out the result before we begin work... + result.clear(); + result.DomainName= domainName; + result.ShardName= shardName; + + // use a set to buildup lists of unique host names + typedef std::set TNameSet; + TNameSet hostNames; + + // fill the result in from our internal data + bool found= false; + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // ignore exes that aren't in the requested domain + if (theApp.DomainName!=domainName) continue; + + // ignore exes that aren't in the requested shard + if (theApp.ShardName!=shardName) continue; + + // add this exe's host to the hosts set + hostNames.insert(theApp.Host); + + // add this exe's unique name to the result apps record + result.Apps.push_back(theApp.UniqueName); + + found=true; + } + + // copy the hosts set to the result record + for (TNameSet::const_iterator it= hostNames.begin(); it!=hostNames.end(); ++it) + { + result.Hosts.push_back(*it); + } + + if (!found) + { + // as a note - if we get here it is because the request has failed - no shard was identified with the given name and domain + // this is not an error case so we just add a debug for info + nldebug("getShard failed for domainName('%s'), shardName('%s')",domainName.c_str(),shardName.c_str()); + } + } + + void CInfoContainer::getApp(const TDomainName& domainName,const TAppName& appName,SAppDescription& result) const + { + // clear out the result before we begin work... + result.clear(); + result.DomainName= domainName; + result.AppName= appName; + + // use a set to buildup lists of unique host names + typedef std::set TNameSet; + TNameSet domainNames; + + // fill the result in from our internal data + for (TExeRecords::const_iterator it= _ExeRecords.begin(); it!=_ExeRecords.end(); ++it) + { + const SExeRecord& theApp= *it; + + // skip exes that aren't in the requested domain + if (theApp.DomainName!=domainName) continue; + + // skip exes that don't have the correct name + if (theApp.UniqueName!=appName) continue; + + // we've found the exe so fill in the result record... + result.ShardName= theApp.ShardName; + result.HostName= theApp.Host; + result.StartOrder= theApp.StartOrder; + result.CmdLine= theApp.CmdLine; + + // setup the config file to start initialised with the app name for this app + result.CfgFile= + "// Auto generated config file\n" + "// Use with commandline: "+theApp.CmdLine+"\n" + "AESAliasName= \""+appName+"\";\n" + "\n"; + + // copy the cfg set to the result record (the cfgAfter set should have been merged in already) + for (SExeRecord::TCfgEntries::const_iterator cit= theApp.CfgEntries.begin(); cit!=theApp.CfgEntries.end(); ++cit) + { + result.CfgFile+=*cit; + result.CfgFile+='\n'; + } + + // copy the dataEntries set to the result record + for (SExeRecord::TDataEntries::const_iterator dit= theApp.DataEntries.begin(); dit!=theApp.DataEntries.end(); ++dit) + { + result.DataPacks.push_back(*dit); + } + + // we're all done so we can return merrily + return; + } + + // as a note - if we get here it is because the request has failed - no app was identified with the given name and domain + // this is not an error case so we just add a debug for info + nldebug("getApp failed for domainName('%s'), appName('%s')",domainName.c_str(),appName.c_str()); + } + + void CInfoContainer::dumpDomains(NLMISC::CLog& log) const + { + for (TDomainExes::const_iterator dit= _DomainExes.begin(); dit!=_DomainExes.end(); ++dit) + { + const NLMISC::CSString& domainName= dit->first; + const TShardExes& shards= dit->second; + log.displayNL("-- domain: %s",domainName.c_str()); + + for (TShardExes::const_iterator sit= shards.begin(); sit!=shards.end(); ++sit) + { + const NLMISC::CSString& shardName= sit->first; + const TExeIdx& exeIdx= sit->second; + log.displayNL(" -- shard: %s",shardName.c_str()); + + for (TExeIdx::const_iterator eit= exeIdx.begin(); eit!=exeIdx.end(); ++eit) + { + uint32 idx= *eit; + nlassert(idx<_ExeRecords.size()); + const SExeRecord& theExe= _ExeRecords[idx]; + log.displayNL(" -- %s:%s (%s)",theExe.Host.c_str(),theExe.CmdLine.c_str(),theExe.UniqueName.c_str()); + } + } + } + } + + void CInfoContainer::dumpInfoBlocks(NLMISC::CLog& log) const + { + log.displayNL("//------------------------------------------------------------------------------"); + log.displayNL("// Dump of cfg database file contents"); + log.displayNL("//------------------------------------------------------------------------------"); + for (TInfoBlocks::const_iterator it=_InfoBlocks.begin(); it!=_InfoBlocks.end(); ++it) + { + it->second->dump(log); + } + log.displayNL("//------------------------------------------------------------------------------"); + } + + CInfoBlock* CInfoContainer::getInfoBlock(const NLMISC::CSString& name) + { + TInfoBlocks::iterator it= _InfoBlocks.find(name); + return (it==_InfoBlocks.end())? NULL: it->second; + } + + void CInfoContainer::addExe(const SExeRecord& exeRecord,uint32& errors) + { + // nldebug("Adding CmdLine: %s",exeRecord.FullName.c_str()); + + // note: if we hit errors then we continue anyway to make sure we display a complete set of error messages + DROP_IF(exeRecord.DomainName.empty(), "No 'domain' property found in: "+exeRecord.FullName, ++errors ); + DROP_IF(exeRecord.ShardName.empty(), "No 'shard' property found in: "+exeRecord.FullName, ++errors ); + DROP_IF(exeRecord.CmdLine.empty(), "No 'cmdLine' property found in: "+exeRecord.FullName, ++errors ); + DROP_IF(exeRecord.Host.empty(), "No 'host' property found in: "+exeRecord.FullName, ++errors ); + DROP_IF(exeRecord.CfgEntries.empty(), "No 'cfg' entriesfound in: "+exeRecord.FullName, ++errors ); + + // add a refference from the domains' shard map to the exe... + _DomainExes[exeRecord.DomainName][exeRecord.ShardName].push_back(_ExeRecords.size()); + + // we may have hit errors but we go ahead anyway as in the case of errors the whole thing will be cleared out anyway + _ExeRecords.push_back(exeRecord); + } + + //----------------------------------------------------------------------------- + // methods CDeploymentConfigurationImplementation + //----------------------------------------------------------------------------- + + bool CDeploymentConfigurationImplementation::read(const NLMISC::CSString& fileName) + { + // setup a temp container to hold the version of the file that we're reading + CInfoContainer container; + + // do the reading and make sure we catch any possible execeptions (like for read in progress) + try + { + container.read(fileName); + } + catch(...) + { + container.clear(); + } + + // if the read failed for whatever reason then giveup + DROP_IF(container.empty(),"Failed to update deployment configuration from file: "+fileName,return false); + + // copy the temp container into our internal object + _InfoContainer= container; + + // display a funky victory message + nlinfo("Deployment configuration successfully updated from file: %s",fileName.c_str()); + + return true; + } + + void CDeploymentConfigurationImplementation::write(const NLMISC::CSString& fileName) + { + // create a displayer to gather the output of the command + class CStringDisplayer: public IDisplayer + { + public: + NLMISC::CSString Data; + void doDisplay( const CLog::TDisplayInfo& args, const char *message) + { + Data += message; + } + }; + + // instantiate the displayer and a log object and assign one to the other + CStringDisplayer stringDisplayer; + NLMISC::CLog myLog; + myLog.addDisplayer(&stringDisplayer); + + // dump the info blocks to our log object (accumulating the result as a string) + dumpInfoBlocks(myLog); + + // write the text accumulated in the log object to a text file + stringDisplayer.Data.writeToFile(fileName); + } + + void CDeploymentConfigurationImplementation::serial(NLMISC::IStream& stream) + { + // setup a temp container to hold the version of the file that we're reading (if we're reading) + // and fill the container in with our internal object just in case we're writing + CInfoContainer container= _InfoContainer; + + // do the serial and make sure we catch any possible execeptions (like for read in progress) + try + { + stream.serial(container); + } + catch(...) + { + container.clear(); + } + + // if the serial failed for whatever reason then giveup + DROP_IF(container.empty(),"Failed to serial deployment configuration: ",return); + + // copy the temp container into our internal object (incase this was a read operation) + _InfoContainer= container; + + // display a funky victory message + nlinfo("Deployment configuration successfully serialised"); + } + + void CDeploymentConfigurationImplementation::getDomainNames(TDomainNames& result) const + { + _InfoContainer.getDomainNames(result); + } + + void CDeploymentConfigurationImplementation::getShardNames(const TDomainName& domainName,TShardNames& result) const + { + _InfoContainer.getShardNames(domainName,result); + } + + void CDeploymentConfigurationImplementation::getHostNames(THostNames& result) const + { + _InfoContainer.getHostNames(result); + } + + void CDeploymentConfigurationImplementation::getAppNames(const THostName& hostName,const TDomainName& domainName,TAppNames& result) const + { + _InfoContainer.getAppNames(hostName,domainName,result); + } + + void CDeploymentConfigurationImplementation::getAppNames(const THostName& hostName,const TDomainName& domainName,const TShardName& shardName,TAppNames& result) const + { + _InfoContainer.getAppNames(hostName,domainName,shardName,result); + } + + void CDeploymentConfigurationImplementation::getHost(const THostName& hostName,SHostDescription& result) const + { + _InfoContainer.getHost(hostName,result); + } + + void CDeploymentConfigurationImplementation::getDomain(const TDomainName& domainName, SDomainDescription& result) const + { + _InfoContainer.getDomain(domainName,result); + } + + void CDeploymentConfigurationImplementation::getShard(const TDomainName& domainName,const TShardName& shardName,SShardDescription& result) const + { + _InfoContainer.getShard(domainName,shardName,result); + } + + void CDeploymentConfigurationImplementation::getApp(const TDomainName& domainName,const TAppName& appName,SAppDescription& result) const + { + _InfoContainer.getApp(domainName,appName,result); + } + + void CDeploymentConfigurationImplementation::dumpInfoBlocks(NLMISC::CLog& log) const + { + _InfoContainer.dumpInfoBlocks(log); + } + + void CDeploymentConfigurationImplementation::dumpDomains(NLMISC::CLog& log) const + { + _InfoContainer.dumpDomains(log); + } + + + //----------------------------------------------------------------------------- + // methods CDeploymentConfiguration + //----------------------------------------------------------------------------- + + CDeploymentConfiguration& CDeploymentConfiguration::getInstance() + { + return CSingleton::getInstance(); + } + +} // end of namespace + +//NLMISC_CATEGORISED_COMMAND(depcfg,readDepCfgFile,"(re)read the deployment cfg file","[=\"server_park_database.txt\"]") +//{ +// NLMISC::CSString fileName= defaultDeploymentConfigurationFileName; +// +// switch (args.size()) +// { +// case 1: +// fileName=args[0]; +// break; +// +// case 0: +// break; +// +// default: +// return false; +// } +// +// DEPCFG::CDeploymentConfiguration::getInstance().read(fileName); +// +// return true; +//} +// +//NLMISC_CATEGORISED_COMMAND(depcfg,writeDepCfgFile,"write the deployment cfg file","[=\"saved_server_park_database.txt\"]") +//{ +// NLMISC::CSString fileName= CSString("saved_") + defaultDeploymentConfigurationFileName; +// +// switch (args.size()) +// { +// case 1: +// fileName=args[0]; +// break; +// +// case 0: +// break; +// +// default: +// return false; +// } +// +// DEPCFG::CDeploymentConfiguration::getInstance().read(fileName); +// +// return true; +//} +// +//NLMISC_CATEGORISED_COMMAND(depcfg,saveDepCfgBinary,"write a binary version of the deployment file to disk","") +//{ +// if (args.size()!=1) +// return false; +// +// NLMISC::COFile outf(args[0]); +// outf.serial(DEPCFG::CDeploymentConfiguration::getInstance()); +// +// return true; +//} +// +//NLMISC_CATEGORISED_COMMAND(depcfg,loadDepCfgBinary,"read a binary version of the deployment file from disk","") +//{ +// if (args.size()!=1) +// return false; +// +// NLMISC::CIFile inf(args[0]); +// inf.serial(DEPCFG::CDeploymentConfiguration::getInstance()); +// +// return true; +//} + +NLMISC_CATEGORISED_COMMAND(depcfg,dumpDepCfgInfoBlocks,"dump the raw info blocks for the deployment config singleton","") +{ + if (args.size()!=0) + return false; + + DEPCFG::CDeploymentConfiguration::getInstance().dumpInfoBlocks(log); + + return true; +} + +NLMISC_CATEGORISED_COMMAND(depcfg,dumpDepCfgDomains,"dump the domain set for the deployment config singleton","") +{ + if (args.size()!=0) + return false; + + DEPCFG::CDeploymentConfiguration::getInstance().dumpDomains(log); + + return true; +} + +NLMISC_CATEGORISED_COMMAND(depcfg,dumpDepCfgHosts,"dump the host set for the deployment config singleton","") +{ + if (args.size()!=0) + return false; + + log.displayNL("--------------------------------------------"); + log.displayNL("Hosts"); + log.displayNL("--------------------------------------------"); + + DEPCFG::THostNames hostNames; + DEPCFG::CDeploymentConfiguration::getInstance().getHostNames(hostNames); + sort(hostNames.begin(),hostNames.end()); + for (DEPCFG::THostNames::iterator hit= hostNames.begin(); hit!=hostNames.end(); ++hit) + { + DEPCFG::SHostDescription host; + DEPCFG::CDeploymentConfiguration::getInstance().getHost(*hit,host); + log.displayNL("Host %s (%d domains)",host.HostName.c_str(),host.Domains.size()); + + for (DEPCFG::TDomainNames::iterator dit= host.Domains.begin(); dit!=host.Domains.end(); ++dit) + { + log.displayNL("-- Domain %s",dit->c_str()); + } + } + + return true; +} + +NLMISC_CATEGORISED_COMMAND(depcfg,dumpDepCfgShards,"dump the shard set for the deployment config singleton","") +{ + if (args.size()!=0) + return false; + + log.displayNL("--------------------------------------------"); + log.displayNL("Shards"); + log.displayNL("--------------------------------------------"); + + DEPCFG::TDomainNames domainNames; + DEPCFG::CDeploymentConfiguration::getInstance().getDomainNames(domainNames); + sort(domainNames.begin(),domainNames.end()); + for (DEPCFG::TDomainNames::iterator dit= domainNames.begin(); dit!=domainNames.end(); ++dit) + { + DEPCFG::SDomainDescription domain; + DEPCFG::CDeploymentConfiguration::getInstance().getDomain(*dit,domain); + log.displayNL("Domain %s (%d shards with %d apps on %d hosts)",domain.DomainName.c_str(),domain.Shards.size(),domain.Apps.size(),domain.Hosts.size()); + + DEPCFG::TShardNames shardNames; + DEPCFG::CDeploymentConfiguration::getInstance().getShardNames(*dit,shardNames); + nlassert(shardNames==domain.Shards); + for (DEPCFG::TShardNames::iterator sit= shardNames.begin(); sit!=shardNames.end(); ++sit) + { + DEPCFG::SShardDescription shard; + DEPCFG::CDeploymentConfiguration::getInstance().getShard(*dit,*sit,shard); + log.displayNL("-- Shard %s/%s (%d apps on %d hosts)",shard.DomainName.c_str(),shard.ShardName.c_str(),shard.Apps.size(),shard.Hosts.size()); + + for (DEPCFG::THostNames::iterator hit= shard.Hosts.begin(); hit!=shard.Hosts.end(); ++hit) + { + log.displayNL(" -- Host %s",hit->c_str()); + DEPCFG::TAppNames appNames; + DEPCFG::CDeploymentConfiguration::getInstance().getAppNames(*hit,*dit,*sit,appNames); + for (DEPCFG::TAppNames::iterator ait= appNames.begin(); ait!=appNames.end(); ++ait) + { + DEPCFG::SAppDescription app; + DEPCFG::CDeploymentConfiguration::getInstance().getApp(*dit,*ait,app); + uint32 cfgFileLines=app.CfgFile.countLines(); + uint32 numDataPacks= app.DataPacks.size(); + log.displayNL(" -- App: %-20s: %s (cfg file length: %d lines, data packs used: %d)",app.AppName.c_str(),app.CmdLine.c_str(),cfgFileLines,numDataPacks); + } + } + } + } + + return true; +} diff --git a/code/ryzom/common/src/game_share/deployment_configuration.h b/code/ryzom/common/src/game_share/deployment_configuration.h new file mode 100644 index 000000000..f79a36410 --- /dev/null +++ b/code/ryzom/common/src/game_share/deployment_configuration.h @@ -0,0 +1,255 @@ +/** \file deployment_configuration.h + * + * + */ + +#ifndef DEPLOYMENT_CONFIGURATION_H +#define DEPLOYMENT_CONFIGURATION_H + +//----------------------------------------------------------------------------- +// include +//----------------------------------------------------------------------------- + +// nel +#include "nel/misc/types_nl.h" +#include "nel/misc/common.h" +#include "nel/misc/sstring.h" + + +//----------------------------------------------------------------------------- +// namespace DEPCFG +//----------------------------------------------------------------------------- + +namespace DEPCFG +{ + //----------------------------------------------------------------------------- + // some handy constants + //----------------------------------------------------------------------------- + + #define defaultDeploymentConfigurationFileName "server_park_database.txt" + + + //----------------------------------------------------------------------------- + // some handy typedefs for identifiers, map keys, etc + //----------------------------------------------------------------------------- + + typedef NLMISC::CSString TDomainName; + typedef NLMISC::CSString TShardName; + typedef NLMISC::CSString THostName; + typedef NLMISC::CSString TAppName; + + typedef NLMISC::CVectorSString TDomainNames; + typedef NLMISC::CVectorSString TShardNames; + typedef NLMISC::CVectorSString THostNames; + typedef NLMISC::CVectorSString TAppNames; + + typedef NLMISC::CSString TCmdLine; + typedef NLMISC::CSString TCfgFile; + typedef NLMISC::CVectorSString TDataPacks; + + + //----------------------------------------------------------------------------- + // struct SDomainDescription + //----------------------------------------------------------------------------- + + struct SDomainDescription + { + //------------------------------------------------------------------------- + // identifying the domain + TDomainName DomainName; + //------------------------------------------------------------------------- + // the set of shards and apps belonging to this domain and their hosts + TShardNames Shards; + THostNames Hosts; + TAppNames Apps; + + //------------------------------------------------------------------------- + // a handy 'clear()' method + void clear() + { + DomainName.clear(); + Shards.clear(); + Hosts.clear(); + Apps.clear(); + } + }; + + + //----------------------------------------------------------------------------- + // struct SShardDescription + //----------------------------------------------------------------------------- + + struct SShardDescription + { + //------------------------------------------------------------------------- + // identifying the shard by name and domain + TDomainName DomainName; + TShardName ShardName; + //------------------------------------------------------------------------- + // the set of hosts and apps used by this shard + THostNames Hosts; + TAppNames Apps; + + //------------------------------------------------------------------------- + // a handy 'clear()' method + void clear() + { + DomainName.clear(); + ShardName.clear(); + Hosts.clear(); + Apps.clear(); + } + }; + + + //----------------------------------------------------------------------------- + // struct SHostDescription + //----------------------------------------------------------------------------- + + struct SHostDescription + { + //------------------------------------------------------------------------- + // identifying the host (machine's network name) + THostName HostName; + //------------------------------------------------------------------------- + // the set of domains that refference this host + TDomainNames Domains; + + //------------------------------------------------------------------------- + // a handy 'clear()' method + void clear() + { + HostName.clear(); + Domains.clear(); + } + }; + + + //----------------------------------------------------------------------------- + // struct SAppDescription + //----------------------------------------------------------------------------- + + struct SAppDescription + { + //------------------------------------------------------------------------- + // identifying the app, the shard it belongs to, machine it runs on, etc + TDomainName DomainName; + TShardName ShardName; + THostName HostName; + TAppName AppName; + NLMISC::CSString StartOrder; + + //------------------------------------------------------------------------- + // some real data + TCmdLine CmdLine; // the command line to execute the app + TCfgFile CfgFile; // the cfg file contents for the app + TDataPacks DataPacks; // the set of data packs for the app + + //------------------------------------------------------------------------- + // a handy 'clear()' method + void clear() + { + DomainName.clear(); + ShardName.clear(); + HostName.clear(); + AppName.clear(); + StartOrder.clear(); + CmdLine.clear(); + CfgFile.clear(); + DataPacks.clear(); + } + }; + + + //----------------------------------------------------------------------------- + // class CDeploymentConfiguration + //----------------------------------------------------------------------------- + + class CDeploymentConfiguration + { + public : + //------------------------------------------------------------------------- + // this is a singleton so we have a getInstance() method + // remaining methods are pure virtuals... + + // get hold of the instance of the singleton that derives from CDeploymentConfiguration + static CDeploymentConfiguration& getInstance(); + + + //------------------------------------------------------------------------- + // methods for reading / serialising deployment configuration + + // read the deployment configuration from a specified file + // + // file format is line base with the following format: + // '//'... comment to end of line + // 'define' // define a new block + // 'domain' // flag this block as a domain and give it a name + // 'shard' // flag this block as a shard and give it a name + // 'use' // add a child block to this block + // 'name' // set the unique name to use for the executables derived from this block + // 'cmdLine' // setup the command line to use for exes derived from this block + // 'host' // assign the exes derived from this block to a given host + // 'cfg' // add a line to be included in the cfg file of executables derived from this block + // 'cfgAfter' // as above but cfg lines appended at end of cfg file (not at start) + // 'data' // add dependency on a given data block to exes derived from this block + // + virtual bool read(const NLMISC::CSString& fileName) =0; + + // write the info blocks out to a file (in no particular order) + // the output file can be re-read via the 'read()' method + virtual void write(const NLMISC::CSString& fileName) =0; + + // serialise the deployment configuration, for dispatch to / reception from other apps + virtual void serial(NLMISC::IStream& stream) =0; + + + //------------------------------------------------------------------------- + // read accessors - getting sets of names + + // get the complete set of hosts (machines on which we have apps running) + virtual void getHostNames(THostNames& result) const =0; + + // get the complete set of domains + virtual void getDomainNames(TDomainNames& result) const =0; + + // get the shards for a given domain + virtual void getShardNames(const TDomainName& domainName,TShardNames& result) const =0; + + // get the apps for a given host and domain + virtual void getAppNames(const THostName& hostName,const TDomainName& domainName,TAppNames& result) const =0; + + // get the apps for a given host, domain and shard + virtual void getAppNames(const THostName& hostName,const TDomainName& domainName,const TShardName& shardName,TAppNames& result) const =0; + + + //------------------------------------------------------------------------- + // read accessors - getting info structure for a named object + + // get a named host + virtual void getHost(const THostName& hostName,SHostDescription& result) const =0; + + // get a named domains + virtual void getDomain(const TDomainName& domainName, SDomainDescription& result) const =0; + + // get a named shard for a given domain + virtual void getShard(const TDomainName& domainName,const TShardName& shardName,SShardDescription& result) const =0; + + // get a named app for a given domain + virtual void getApp(const TDomainName& domainName,const TAppName& appName,SAppDescription& result) const =0; + + + //------------------------------------------------------------------------- + // methods for dumping info to a given log + + // dump raw information (organised by info block, not by domain or host) + virtual void dumpInfoBlocks(NLMISC::CLog& log) const =0; + + // dump info organised by domain + virtual void dumpDomains(NLMISC::CLog& log) const =0; + }; + +} // end of namespace + +//----------------------------------------------------------------------------- +#endif