(Retrofit from trunk) Small setup refactoring for getting ready for the Hub.

SVN:2.4[5271]
This commit is contained in:
Denis Flaven
2018-01-17 10:13:14 +00:00
parent f237a98c1d
commit c081b89f03
6 changed files with 250 additions and 171 deletions

View File

@@ -615,16 +615,20 @@ class utils
{
if (self::$oConfig == null)
{
$sConfigFile = self::GetConfigFilePath();
if (file_exists($sConfigFile))
{
self::$oConfig = new Config($sConfigFile);
}
else
{
// When executing the setup, the config file may be still missing
self::$oConfig = new Config();
}
self::$oConfig = MetaModel::GetConfig();
if (self::$oConfig == null)
{
$sConfigFile = self::GetConfigFilePath();
if (file_exists($sConfigFile))
{
self::$oConfig = new Config($sConfigFile);
}
else
{
// When executing the setup, the config file may be still missing
self::$oConfig = new Config();
}
}
}
return self::$oConfig;
}

View File

@@ -547,7 +547,7 @@ class SQLObjectQuery extends SQLQuery
return $iRet;
}
protected function CollectUsedTables(&$aTables)
public function CollectUsedTables(&$aTables)
{
$this->m_oConditionExpr->CollectUsedParents($aTables);
foreach($this->m_aFields as $sFieldAlias => $oField)

View File

@@ -1157,7 +1157,7 @@ class UserRights
self::$m_aAdmins = array();
self::$m_aPortalUsers = array();
}
if (!isset($_SESSION))
if (!isset($_SESSION) && !utils::IsModeCLI())
{
session_name('itop-'.md5(APPROOT));
session_start();

View File

@@ -240,8 +240,9 @@ class ApplicationInstaller
$sDBName = $aDBParams['name'];
$sDBPrefix = $aDBParams['prefix'];
$bOldAddon = $this->oParams->Get('old_addon', false);
$sUrl = $this->oParams->Get('url', '');
self::DoUpdateDBSchema($sMode, $aSelectedModules, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment, $bOldAddon);
self::DoUpdateDBSchema($sMode, $aSelectedModules, $sTargetDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment, $bOldAddon, $sUrl);
$aResult = array(
'status' => self::OK,
@@ -555,7 +556,7 @@ class ApplicationInstaller
}
}
protected static function DoUpdateDBSchema($sMode, $aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false)
protected static function DoUpdateDBSchema($sMode, $aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false, $sAppRootUrl = '')
{
SetupPage::log_info("Update Database Schema for environment '$sTargetEnvironment'.");
@@ -568,6 +569,7 @@ class ApplicationInstaller
'db_pwd' => $sDBPwd,
'db_name' => $sDBName,
'db_prefix' => $sDBPrefix,
'application_path' => $sAppRootUrl,
);
$oConfig->UpdateFromParams($aParamValues, $sModulesDir);
if ($bOldAddon)
@@ -577,7 +579,7 @@ class ApplicationInstaller
'user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php',
));
}
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
$oProductionEnv->InitDataModel($oConfig, true); // load data model only
@@ -636,17 +638,7 @@ class ApplicationInstaller
// Module specific actions (migrate the data)
//
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT.$sModulesDir);
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::BeforeDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
$aCallSpec = array($sModuleInstallerClass, 'BeforeDatabaseCreation');
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
}
}
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation');
if(!$oProductionEnv->CreateDatabaseStructure(MetaModel::GetConfig(), $sMode))
{
@@ -778,18 +770,7 @@ class ApplicationInstaller
// Perform here additional DB setup... profiles, etc...
//
$aAvailableModules = $oProductionEnv->AnalyzeInstallation(MetaModel::GetConfig(), APPROOT.$sModulesDir);
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDatabaseCreation(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
// The validity of the sModuleInstallerClass has been established in BuildConfig()
$aCallSpec = array($sModuleInstallerClass, 'AfterDatabaseCreation');
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
}
}
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
$oProductionEnv->UpdatePredefinedObjects();
@@ -807,18 +788,7 @@ class ApplicationInstaller
// Perform final setup tasks here
//
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDatabaseSetup(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
// The validity of the sModuleInstallerClass has been established in BuildConfig()
$aCallSpec = array($sModuleInstallerClass, 'AfterDatabaseSetup');
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
}
}
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
}
/**
@@ -860,131 +830,21 @@ class ApplicationInstaller
));
}
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
//Load the MetaModel if needed (asynchronous mode)
if (!self::$bMetaModelStarted)
{
$oProductionEnv = new RunTimeEnvironment($sTargetEnvironment);
$oProductionEnv->InitDataModel($oConfig, false); // load data model and connect to the database
self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously
}
$oDataLoader = new XMLDataLoader();
CMDBObject::SetTrackInfo("Initialization");
$oMyChange = CMDBObject::GetCurrentChange();
SetupPage::log_info("starting data load session");
$oDataLoader->StartSession($oMyChange);
$aFiles = array();
$aPreviouslyLoadedFiles = array();
$oProductionEnv = new RunTimeEnvironment();
$aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, APPROOT.$sModulesDir);
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE))
{
$sRelativePath = 'env-'.$sTargetEnvironment.'/'.basename($aModule['root_dir']);
// Load data only for selected AND newly installed modules
if (in_array($sModuleId, $aSelectedModules))
{
if ($aModule['version_db'] != '')
{
// Simulate the load of the previously loaded XML files to get the mapping of the keys
if ($bSampleData)
{
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']);
}
else
{
// Load only structural data
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
}
}
else
{
if ($bSampleData)
{
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']);
}
else
{
// Load only structural data
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
}
}
}
}
}
$oProductionEnv->LoadData($aAvailableModules, $aSelectedModules, $bSampleData);
// Simulate the load of the previously loaded files, in order to initialize
// the mapping between the identifiers in the XML and the actual identifiers
// in the current database
foreach($aPreviouslyLoadedFiles as $sFileRelativePath)
{
$sFileName = APPROOT.$sFileRelativePath;
SetupPage::log_info("Loading file: $sFileName (just to get the keys mapping)");
if (empty($sFileName) || !file_exists($sFileName))
{
throw(new Exception("File $sFileName does not exist"));
}
$oDataLoader->LoadFile($sFileName, true);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupPage::log_info($sResult);
}
foreach($aFiles as $sFileRelativePath)
{
$sFileName = APPROOT.$sFileRelativePath;
SetupPage::log_info("Loading file: $sFileName");
if (empty($sFileName) || !file_exists($sFileName))
{
throw(new Exception("File $sFileName does not exist"));
}
$oDataLoader->LoadFile($sFileName);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupPage::log_info($sResult);
}
$oDataLoader->EndSession();
SetupPage::log_info("ending data load session");
// Perform after dbload setup tasks here
//
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::AfterDataLoad(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
// The validity of the sModuleInstallerClass has been established in BuildConfig()
$aCallSpec = array($sModuleInstallerClass, 'AfterDataLoad');
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
}
}
}
/**
* Merge two arrays of file names, adding the relative path to the files provided in the array to merge
* @param string[] $aSourceArray
* @param string $sBaseDir
* @param string[] $aFilesToMerge
* @return string[]
*/
protected static function MergeWithRelativeDir($aSourceArray, $sBaseDir, $aFilesToMerge)
{
$aToMerge = array();
foreach($aFilesToMerge as $sFile)
{
$aToMerge[] = $sBaseDir.'/'.$sFile;
}
return array_merge($aSourceArray, $aToMerge);
// Perform after dbload setup tasks here
//
$oProductionEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDataLoad');
}
protected static function DoCreateConfig($sMode, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sUrl, $sLanguage, $aSelectedModuleCodes, $aSelectedExtensionCodes, $sTargetEnvironment, $bOldAddon, $sSourceDir, $sPreviousConfigFile, $sDataModelVersion, $sGraphvizPath)

View File

@@ -67,6 +67,16 @@ class iTopExtension
*/
public $aModules;
/**
* @var string[]
*/
public $aModuleVersion;
/**
* @var string
*/
public $sSourceDir;
public function __construct()
{
$this->sCode = '';
@@ -78,6 +88,8 @@ class iTopExtension
$this->bMarkedAsChosen = false;
$this->sVersion = ITOP_VERSION;
$this->sInstalledVersion = '';
$this->aModuleVersion = array();
$this->sSourceDir = '';
$this->bVisible = true;
}
}
@@ -240,6 +252,7 @@ class iTopExtensionsMap
$oExtension->sMoreInfoUrl = $oXml->Get('more_info_url');
$oExtension->sVersion = $oXml->Get('version');
$oExtension->sSource = $sSource;
$oExtension->sSourceDir = $sSearchDir;
$sParentExtensionId = $sExtensionId = $oExtension->sCode.'/'.$oExtension->sVersion;
$this->AddExtension($oExtension);
@@ -274,7 +287,8 @@ class iTopExtensionsMap
{
// Already inside an extension, let's add this module the list of modules belonging to this extension
$this->aExtensions[$sParentExtensionId]->aModules[] = $sModuleName;
}
$this->aExtensions[$sParentExtensionId]->aModuleVersion[$sModuleName] = $sModuleVersion;
}
else
{
// Not already inside an folder containing an 'extension.xml' file
@@ -299,10 +313,10 @@ class iTopExtensionsMap
$oExtension->bMandatory = $aModuleInfo[2]['mandatory'];
$oExtension->sMoreInfoUrl = $aModuleInfo[2]['doc.more_information'];
$oExtension->aModules = array($sModuleName);
$oExtension->aModuleVersion[$sModuleName] = $sModuleVersion;
$oExtension->sSourceDir = $sSearchDir;
$oExtension->bVisible = $bVisible;
$this->AddExtension($oExtension);
$this->AddExtension($oExtension);
}
}
}
@@ -479,6 +493,34 @@ class iTopExtensionsMap
return true;
}
/**
* Find is a single-module extension is contained within another extension
* @param iTopExtension $oExtension
* @return NULL|iTopExtension
*/
public function IsExtensionObsoletedByAnother(iTopExtension $oExtension)
{
// Complex extensions (more than 1 module) are never considered as obsolete
if (count($oExtension->aModules) != 1) return null;
foreach($this->GetAllExtensions() as $oOtherExtension)
{
if (($oOtherExtension->sSourceDir != $oExtension->sSourceDir) && ($oOtherExtension->sSource != iTopExtension::SOURCE_WIZARD))
{
if (array_key_exists($oExtension->sCode, $oOtherExtension->aModuleVersion) &&
(version_compare($oOtherExtension->aModuleVersion[$oExtension->sCode], $oExtension->sVersion, '>=')) )
{
// Found another extension containing a more recent version of the extension/module
return $oOtherExtension;
}
}
}
// No match at all
return null;
}
/**
* Search for multi-module extensions that are NOT deployed as an extension (i.e. shipped with an extension.xml file)
* but as a bunch of un-related modules based on the signature of some well-known extensions. If such an extension is found,

View File

@@ -482,6 +482,7 @@ class RunTimeEnvironment
* - plus the list of modules present in the "extra" directory of the target environment: data/<target_environment>-modules/
* @param string $sSourceEnv The name of the source environment to 'imitate'
* @param bool $bUseSymLinks Whether to create symbolic links instead of copies
* @return string[]
*/
public function CompileFrom($sSourceEnv, $bUseSymLinks = false)
{
@@ -492,7 +493,8 @@ class RunTimeEnvironment
// Do load the required modules
//
$oFactory = new ModelFactory($sSourceDirFull);
foreach($this->GetMFModulesToCompile($sSourceEnv, $sSourceDir) as $oModule)
$aModulesToCompile = $this->GetMFModulesToCompile($sSourceEnv, $sSourceDir);
foreach($aModulesToCompile as $oModule)
{
if ($oModule instanceof MFDeltaModule)
{
@@ -543,6 +545,7 @@ class RunTimeEnvironment
MetaModel::ResetCache(md5(APPROOT).'-'.$this->sTargetEnv);
}
return array_keys($aModulesToCompile);
}
/**
@@ -1042,4 +1045,174 @@ class RunTimeEnvironment
}
}
}
/**
* Call the given handler method for all selected modules having an installation handler
* @param array[] $aAvailableModules
* @param string[] $aSelectedModules
* @param string $sHandlerName
*/
public function CallInstallerHandlers($aAvailableModules, $aSelectedModules, $sHandlerName)
{
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE) && in_array($sModuleId, $aSelectedModules) &&
isset($aAvailableModules[$sModuleId]['installer']) )
{
$sModuleInstallerClass = $aAvailableModules[$sModuleId]['installer'];
SetupPage::log_info("Calling Module Handler: $sModuleInstallerClass::$sHandlerName(oConfig, {$aModule['version_db']}, {$aModule['version_code']})");
$aCallSpec = array($sModuleInstallerClass, $sHandlerName);
if (is_callable($aCallSpec))
{
call_user_func_array($aCallSpec, array(MetaModel::GetConfig(), $aModule['version_db'], $aModule['version_code']));
}
}
}
}
/**
* Load data from XML files for the selected modules (structural data and/or sample data)
* @param array[] $aAvailableModules All available modules and their definition
* @param string[] $aSelectedModules List of selected modules
* @param bool $bSampleData Wether or not to load sample data
*/
public function LoadData($aAvailableModules, $aSelectedModules, $bSampleData)
{
$oDataLoader = new XMLDataLoader();
CMDBObject::SetTrackInfo("Initialization");
$oMyChange = CMDBObject::GetCurrentChange();
SetupPage::log_info("starting data load session");
$oDataLoader->StartSession($oMyChange);
$aFiles = array();
$aPreviouslyLoadedFiles = array();
foreach($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId != ROOT_MODULE))
{
$sRelativePath = 'env-'.$this->sTargetEnv.'/'.basename($aModule['root_dir']);
// Load data only for selected AND newly installed modules
if (in_array($sModuleId, $aSelectedModules))
{
if ($aModule['version_db'] != '')
{
// Simulate the load of the previously loaded XML files to get the mapping of the keys
if ($bSampleData)
{
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']);
}
else
{
// Load only structural data
$aPreviouslyLoadedFiles = static::MergeWithRelativeDir($aPreviouslyLoadedFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
}
}
else
{
if ($bSampleData)
{
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.sample']);
}
else
{
// Load only structural data
$aFiles = static::MergeWithRelativeDir($aFiles, $sRelativePath, $aAvailableModules[$sModuleId]['data.struct']);
}
}
}
}
}
// Simulate the load of the previously loaded files, in order to initialize
// the mapping between the identifiers in the XML and the actual identifiers
// in the current database
foreach($aPreviouslyLoadedFiles as $sFileRelativePath)
{
$sFileName = APPROOT.$sFileRelativePath;
SetupPage::log_info("Loading file: $sFileName (just to get the keys mapping)");
if (empty($sFileName) || !file_exists($sFileName))
{
throw(new Exception("File $sFileName does not exist"));
}
$oDataLoader->LoadFile($sFileName, true);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupPage::log_info($sResult);
}
foreach($aFiles as $sFileRelativePath)
{
$sFileName = APPROOT.$sFileRelativePath;
SetupPage::log_info("Loading file: $sFileName");
if (empty($sFileName) || !file_exists($sFileName))
{
throw(new Exception("File $sFileName does not exist"));
}
$oDataLoader->LoadFile($sFileName);
$sResult = sprintf("loading of %s done.", basename($sFileName));
SetupPage::log_info($sResult);
}
$oDataLoader->EndSession();
SetupPage::log_info("ending data load session");
}
/**
* Merge two arrays of file names, adding the relative path to the files provided in the array to merge
* @param string[] $aSourceArray
* @param string $sBaseDir
* @param string[] $aFilesToMerge
* @return string[]
*/
protected static function MergeWithRelativeDir($aSourceArray, $sBaseDir, $aFilesToMerge)
{
$aToMerge = array();
foreach($aFilesToMerge as $sFile)
{
$aToMerge[] = $sBaseDir.'/'.$sFile;
}
return array_merge($aSourceArray, $aToMerge);
}
/**
* Check the MetaModel for some common pitfall (class name too long, classes requiring too many joins...)
* The check takes about 900 ms for 200 classes
* @throws Exception
* @return string
*/
public function CheckMetaModel()
{
$iCount = 0;
$fStart = microtime(true);
foreach(MetaModel::GetClasses() as $sClass)
{
$oSearch = new DBObjectSearch($sClass);
$oSearch->SetShowObsoleteData(false);
$oSQLQuery = $oSearch->GetSQLQueryStructure(null, false);
$sViewName = MetaModel::DBGetView($sClass);
if (strlen($sViewName) > 64)
{
throw new Exception("Class name too long for class: '$sClass'. The name of the corresponding view ($sViewName) would exceed MySQL's limit for the name of a table (64 characters).");
}
$sTableName = MetaModel::DBGetTable($sClass);
if (strlen($sTableName) > 64)
{
throw new Exception("Table name too long for class: '$sClass'. The name of the corresponding MySQL table ($sTableName) would exceed MySQL's limit for the name of a table (64 characters).");
}
$iTableCount = $oSQLQuery->CountTables();
if ($iTableCount > 61)
{
throw new Exception("Class requiring too many tables: '$sClass'. The structure of the class ($sClass) would require a query with more than 61 JOINS (MySQL's limitation).");
}
$iCount++;
}
$fDuration = microtime(true) - $fStart;
return sprintf("Checked %d classes in %.1f ms. No error found.\n", $iCount, $fDuration*1000.0);
}
} // End of class