[--installation_xml=] [--use_itop_config] Options: --param-file= Path to the file (XML) to use for the unattended installation. That file (generated by the setup into log directory) must contain the following sections: - target_env: the target environment (production, test, dev) - database: the database settings (server, user, pwd, name, prefix) - selected_modules: the list of modules to install --response_file DEPRECATED: use `--param-file` instead --installation_xml= Use an installation.xml file to compute the modules to install depending on the selected extensions listed in the param file --use_itop_config Use the iTop configuration file to get the database settings, otherwise use the database settings from the parameters file Advanced options: --check-consistency=1 Check the data model consistency after the installation (default: 0) --clean=1 In case of a first installation, cleanup the environment before proceeding: delete the configuration file, the cache directory, the target directory, the database (default: 0) --install=0 Set to 0 to perform a dry-run (default: 1) EOF; exit(-1); } ///////////////////////////////////////////////// if (! utils::IsModeCLI()) { echo "Mode CLI only"; exit(-1); } if (in_array('--help', $argv)) { PrintUsageAndExit(); } $sParamFile = utils::ReadParam('param-file', null, true /* CLI allowed */, 'raw_data') ?? utils::ReadParam('response_file', null, true /* CLI allowed */, 'raw_data'); if (is_null($sParamFile)) { echo "Missing mandatory argument `--param-file`.\n"; PrintUsageAndExit(); } $bCheckConsistency = (utils::ReadParam('check-consistency', '0', true /* CLI allowed */) == '1'); if (false === file_exists($sParamFile)) { echo "Param file `$sParamFile` doesn't exist! Exiting...\n"; exit(-1); } $oParams = new XMLParameters($sParamFile); $sMode = $oParams->Get('mode'); $sTargetEnvironment = $oParams->Get('target_env', ''); if ($sTargetEnvironment == '') { $sTargetEnvironment = 'production'; } $sXmlSetupBaseName = basename($sParamFile); $sInstallationXmlPath = utils::ReadParam('installation_xml', null, true /* CLI allowed */, 'raw_data'); if (! is_null($sInstallationXmlPath) && is_file($sInstallationXmlPath)) { $sInstallationBaseName = basename($sInstallationXmlPath); $aSelectedExtensionsFromXmlSetup = $oParams->Get('selected_extensions', []); $bInstallationChoicesProvided = count($aSelectedExtensionsFromXmlSetup) !== 0; if ($bInstallationChoicesProvided) { $sMsg = "Modules to install computed based on $sInstallationBaseName file and installation choices (listed in section `selected_extensions` of $sXmlSetupBaseName file)"; echo "$sMsg:\n".implode(',', $aSelectedExtensionsFromXmlSetup)."\n\n"; SetupLog::Info($sMsg, null, $aSelectedExtensionsFromXmlSetup); } else { $sMsg = "Modules to install computed based on default installation choices inside $sInstallationBaseName (no choice specified in section `selected_extensions` of $sXmlSetupBaseName file)."; echo "$sMsg\n\n"; SetupLog::Info($sMsg); } $oInstallationFileService = new InstallationFileService($sInstallationXmlPath, $sTargetEnvironment, $aSelectedExtensionsFromXmlSetup); $oInstallationFileService->Init(); $aComputedExtensions = $oInstallationFileService->GetAfterComputationSelectedExtensions(); if (! $bInstallationChoicesProvided) { $sMsg = "Computed installation choices"; echo "$sMsg:\n".implode(',', $aComputedExtensions)."\n\n"; SetupLog::Info($sMsg, null, $aComputedExtensions); $oParams->Set('selected_extensions', $aComputedExtensions); } $aComputedModules = $oInstallationFileService->GetSelectedModules(); $aSelectedModules = array_keys($aComputedModules); $oParams->Set('selected_modules', $aSelectedModules); $sMsg = "Computed modules to install"; } else { $aSelectedModules = $oParams->Get('selected_modules', []); $sMsg = "Modules to install listed in $sXmlSetupBaseName (selected_modules section)"; } sort($aSelectedModules); echo "$sMsg:\n".implode(',', $aSelectedModules)."\n\n"; SetupLog::Info($sMsg, null, $aSelectedModules); // Configuration file $sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE; $bUseItopConfig = in_array('--use_itop_config', $argv); if ($bUseItopConfig && file_exists($sConfigFile)){ //unattended run based on db settings coming from itop configuration copy($sConfigFile, "$sConfigFile.backup"); $oConfig = new Config($sConfigFile); $aDBXmlSettings = $oParams->Get('database', array()); $aDBXmlSettings ['server'] = $oConfig->Get('db_host'); $aDBXmlSettings ['user'] = $oConfig->Get('db_user'); $aDBXmlSettings ['pwd'] = $oConfig->Get('db_pwd'); $aDBXmlSettings ['name'] = $oConfig->Get('db_name'); $aDBXmlSettings ['prefix'] = $oConfig->Get('db_subname'); $aDBXmlSettings ['db_tls_enabled'] = $oConfig->Get('db_tls.enabled'); //cannot be null or infinite loop triggered! $aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? ""; $oParams->Set('database', $aDBXmlSettings); $aFields = [ 'url' => 'app_root_url', 'source_dir' => 'source_dir', 'graphviz_path' => 'graphviz_path', ]; foreach($aFields as $sSetupField => $sConfField){ $oParams->Set($sSetupField, $oConfig->Get($sConfField)); } $oParams->Set('mysql_bindir', $oConfig->GetModuleSetting('itop-backup', 'mysql_bindir', "")); $oParams->Set('language', $oConfig->GetDefaultLanguage()); } else { //unattended run based on db settings coming from response_file (XML file) $aDBXmlSettings = $oParams->Get('database', array()); } $sDBServer = $aDBXmlSettings['server']; $sDBUser = $aDBXmlSettings['user']; $sDBPwd = $aDBXmlSettings['pwd']; $sDBName = $aDBXmlSettings['name']; $sDBPrefix = $aDBXmlSettings['prefix']; if ($sMode == 'install') { echo "Installation mode detected.\n"; $bClean = utils::ReadParam('clean', false, true /* CLI allowed */); if ($bClean) { echo "Cleanup mode detected.\n"; if (file_exists($sConfigFile)) { echo "Trying to delete the configuration file: '$sConfigFile'.\n"; @chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others unlink($sConfigFile); } else { echo "No config file to delete ($sConfigFile does not exist).\n"; } // Starting with iTop 2.7.0, a failed setup leaves some lock files, let's remove them $aLockFiles = array( 'data/.readonly' => 'read-only lock file', 'data/.maintenance' => 'maintenance mode lock file', ); foreach($aLockFiles as $sFile => $sDescription) { $sLockFile = APPROOT.$sFile; if (file_exists($sLockFile)) { echo "Trying to delete the $sDescription: '$sLockFile'.\n"; unlink($sLockFile); } } // Starting with iTop 2.6.0, let's remove the cache directory as well // Can cause some strange issues in the setup (apparently due to the Dict class being automatically loaded ??) $sCacheDir = APPROOT.'data/cache-'.$sTargetEnvironment; if (file_exists($sCacheDir)) { if (is_dir($sCacheDir)) { echo "Emptying the cache directory '$sCacheDir'.\n"; SetupUtils::tidydir($sCacheDir); } else { die("ERROR the cache directory '$sCacheDir' exists, but is NOT a directory !!!\nExiting.\n"); } } // env-xxx directory $sTargetDir = APPROOT.'env-'.$sTargetEnvironment; if (file_exists($sTargetDir)) { if (is_dir($sTargetDir)) { echo "Emptying the target directory '$sTargetDir'.\n"; SetupUtils::tidydir($sTargetDir); } else { die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n"); } } else { echo "No target directory to delete ($sTargetDir does not exist).\n"; } if ($sDBPrefix != '') { die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting."); } $oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd); if ($oMysqli->connect_errno) { die("Cannot connect to the MySQL server (".$oMysqli->connect_errno . ") ".$oMysqli->connect_error."\nExiting"); } else { if ($oMysqli->select_db($sDBName)) { echo "Deleting database '$sDBName'\n"; $oMysqli->query("DROP DATABASE `$sDBName`"); } else { echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n"; } } } } else { //use settings from itop conf $sTargetEnvironment = $oParams->Get('target_env', ''); if ($sTargetEnvironment == '') { $sTargetEnvironment = 'production'; } $sTargetDir = APPROOT.'env-'.$sTargetEnvironment; } $bHasErrors = false; $aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir $aSelectedModules = $oParams->Get('selected_modules'); $sSourceDir = $oParams->Get('source_dir', 'datamodels/latest'); $sExtensionDir = $oParams->Get('extensions_dir', 'extensions'); $aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules)); foreach($aChecks as $oCheckResult) { switch ($oCheckResult->iSeverity) { case CheckResult::ERROR: $bHasErrors = true; $sHeader = "Error"; break; case CheckResult::WARNING: $sHeader = "Warning"; break; case 3: // CheckResult::TRACE added in iTop 3.0.0 // does nothing : those are old debug traces, see N°2214 $sHeader = 'Trace'; break; case CheckResult::INFO: default: $sHeader = "Info"; break; } echo $sHeader.": ".$oCheckResult->sLabel; if (strlen($oCheckResult->sDescription)) { echo ' - '.$oCheckResult->sDescription; } echo "\n"; } if ($bHasErrors) { echo "Encountered stopper issues. Aborting...\n"; $sLogMsg = "Encountered stopper issues. Aborting..."; echo "$sLogMsg\n"; SetupLog::Error($sLogMsg); exit(-1); } $bFoundIssues = false; $bInstall = utils::ReadParam('install', true, true /* CLI allowed */); if ($bInstall) { echo "Starting the unattended installation...\n"; $oWizard = new ApplicationInstaller($oParams); $bRes = $oWizard->ExecuteAllSteps(); if (!$bRes) { echo "\nencountered installation issues!"; $bFoundIssues = true; } else { $oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd); if (!$oMysqli->connect_errno) { if ($oMysqli->select_db($sDBName)) { // Check the presence of a table to record information about the MTP (from the Designer) $sDesignerUpdatesTable = $sDBPrefix.'priv_designer_update'; $sSQL = "SELECT id FROM `$sDesignerUpdatesTable`"; if ($oMysqli->query($sSQL) !== false) { // Record the Designer Udpates in the priv_designer_update table $sDeltaFile = APPROOT.'data/'.$sTargetEnvironment.'.delta.xml'; if (is_readable($sDeltaFile)) { // Retrieve the revision $oDoc = new DOMDocument(); $oDoc->load($sDeltaFile); $iRevision = 0; $iRevision = $oDoc->firstChild->getAttribute('revision_id'); if ($iRevision > 0) // Safety net, just in case... { $sDate = date('Y-m-d H:i:s'); $sSQL = "INSERT INTO `$sDesignerUpdatesTable` (revision_id, compilation_date, comment) VALUES ($iRevision, '$sDate', 'Deployed using unattended.php.')"; if ($oMysqli->query($sSQL) !== false) { echo "\nDesigner update (MTP at revision $iRevision) successfully recorded.\n"; } else { echo "\nFailed to record designer updates(".$oMysqli->error.").\n"; } } else { echo "\nFailed to read the revision from $sDeltaFile file. No designer update information will be recorded.\n"; } } else { echo "\nNo $sDeltaFile file (or the file is not accessible). No designer update information to record.\n"; } } } } } } else { echo "No installation requested.\n"; } if (!$bFoundIssues && $bCheckConsistency) { echo "Checking data model consistency.\n"; ob_start(); $sCheckRes = ''; try { MetaModel::CheckDefinitions(false); $sCheckRes = ob_get_clean(); } catch(Exception $e) { $sCheckRes = ob_get_clean()."\nException: ".$e->getMessage(); } if (strlen($sCheckRes) > 0) { echo $sCheckRes; echo "\nfound consistency issues!"; $bFoundIssues = true; } } if (! $bFoundIssues) { // last line: used to check the install // the only way to track issues in case of Fatal error or even parsing error! $sLogMsg = "installed!"; if ($bUseItopConfig && is_file("$sConfigFile.backup")) { echo "\nuse config file provided by backup in $sConfigFile."; copy("$sConfigFile.backup", $sConfigFile); } SetupLog::Info($sLogMsg); echo "\n$sLogMsg"; exit(0); } $sLogMsg = "installation failed!"; SetupLog::Error($sLogMsg); echo "\n$sLogMsg\n"; exit(-1);