mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
416 lines
13 KiB
PHP
416 lines
13 KiB
PHP
<?php
|
|
|
|
require_once(dirname(__FILE__, 3) . '/approot.inc.php');
|
|
require_once(__DIR__ . '/InstallationFileService.php');
|
|
|
|
function PrintUsageAndExit()
|
|
{
|
|
echo <<<EOF
|
|
Usage: php unattended-install.php --param-file=<path_to_response_file> [--installation_xml=<path_to_installation_xml>] [--use_itop_config]
|
|
|
|
Options:
|
|
--param-file=<path_to_response_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=<path_to_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);
|
|
}
|
|
/////////////////////////////////////////////////
|
|
|
|
$oCtx = new ContextTag(ContextTag::TAG_SETUP);
|
|
|
|
$sCleanName = strtolower(trim(PHP_SAPI));
|
|
if ($sCleanName !== 'cli')
|
|
{
|
|
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'];
|
|
$bDBTlsEnabled = $aDBXmlSettings['db_tls_enabled'];
|
|
$sDBTlsCa = $aDBXmlSettings['db_tls_ca'];
|
|
|
|
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.");
|
|
}
|
|
|
|
try
|
|
{
|
|
$oMysqli = CMDBSource::GetMysqliInstance($sDBServer, $sDBUser, $sDBPwd, null, $bDBTlsEnabled, $sDBTlsCa, true);
|
|
|
|
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";
|
|
}
|
|
}
|
|
catch (MySQLException $e)
|
|
{
|
|
die($e->getMessage()."\nExiting");
|
|
}
|
|
}
|
|
}
|
|
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
|
|
{
|
|
try
|
|
{
|
|
$oMysqli = CMDBSource::GetMysqliInstance($sDBServer, $sDBUser, $sDBPwd, null, $bDBTlsEnabled, $sDBTlsCa, true);
|
|
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";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (MySQLException $e)
|
|
{
|
|
// Continue anyway
|
|
}
|
|
}
|
|
}
|
|
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);
|