This commit is contained in:
root
2025-11-13 19:04:05 +03:00
commit 240d0aba5f
75129 changed files with 11118122 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
<?php
namespace Bitrix\Scale;
use Bitrix\Main\IO\File;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class Action
* @package Bitrix\Scale
*/
class Action
{
protected $id = "";
protected $userParams = array();
protected $freeParams = array();
protected $actionParams = array();
protected $serverHostname = "";
protected $shellAdapter = null;
protected $result = array();
protected $logLevel = Logger::LOG_LEVEL_INFO;
/**
* @param string $actionId
* @param array $actionParams
* @param string $serverHostname
* @param array $userParams
* @param array $freeParams
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Bitrix\Main\ArgumentTypeException
* @throws \Exception
*/
public function __construct($actionId, $actionParams, $serverHostname="", $userParams = array(), $freeParams = array())
{
if($actionId == '')
throw new \Bitrix\Main\ArgumentNullException("actionId");
if(!is_array($actionParams) || empty($actionParams))
throw new \Exception("Params of action ".$actionId." are not defined correctly!");
if(!isset($actionParams["START_COMMAND_TEMPLATE"]) || $actionParams["START_COMMAND_TEMPLATE"] == '')
throw new \Exception("Required param START_COMMAND_TEMPLATE of action ".$actionId." are not defined!");
if(!is_array($userParams))
throw new \Bitrix\Main\ArgumentTypeException("userParams", "array");
if(!is_array($freeParams))
throw new \Bitrix\Main\ArgumentTypeException("freeParams", "array");
$this->id = $actionId;
$this->userParams = $userParams;
$this->freeParams = $freeParams;
$this->actionParams = $actionParams;
$this->serverHostname = $serverHostname;
$this->shellAdapter = new ShellAdapter;
if(isset($actionParams["LOG_LEVEL"]))
$this->logLevel = $actionParams["LOG_LEVEL"];
}
protected function getServerParams()
{
return ServersData::getServer($this->serverHostname);
}
/**
* Makes command for shell action execution
* @param array $inputParams
* @return string - command to execute
* @throws \Bitrix\Main\ArgumentTypeException
*/
protected function makeStartCommand($inputParams = array())
{
if(!is_array($inputParams))
throw new \Bitrix\Main\ArgumentTypeException("inputParams", "array");
$retStr = $this->actionParams["START_COMMAND_TEMPLATE"];
foreach ($this->userParams as $key => $paramValue)
{
if($this->actionParams['USER_PARAMS'][$key]['THROUGH_FILE'] == 'Y')
{
if($paramValue <> '')
{
$tmpDir = Helper::getTmpDir();
$tmpFile = $tmpDir.'/.'.randString();
$res = File::putFileContents($tmpFile, $paramValue);
if($res === false)
return '';
$paramValue = $tmpFile;
}
}
$retStr = str_replace('##USER_PARAMS:'.$key.'##', $paramValue, $retStr);
}
if($this->serverHostname <> '' && $this->serverHostname != "global")
{
$serverParams = $this->getServerParams();
$serverParams["hostname"] = $this->serverHostname;
if(is_array($serverParams))
{
foreach ($serverParams as $key => $paramValue)
{
if(is_string($paramValue))
{
$retStr = str_replace('##SERVER_PARAMS:' . $key . '##', $paramValue, $retStr);
}
}
}
}
if(!empty($inputParams))
foreach ($inputParams as $key => $paramValue)
$retStr = str_replace('##INPUT_PARAMS:'.$key.'##', $paramValue, $retStr);
if(isset($this->actionParams["CODE_PARAMS"]) && is_array($this->actionParams["CODE_PARAMS"]))
{
foreach($this->actionParams["CODE_PARAMS"] as $paramId => $paramCode)
{
$res = eval($paramCode);
$retStr = str_replace('##CODE_PARAMS:'.$paramId.'##', $res, $retStr);
}
}
foreach ($this->freeParams as $key => $paramValue)
$retStr = str_replace('##'.$key.'##', $paramValue, $retStr);
return $retStr;
}
/**
* Starts the action execution
* @param array $inputParams - params from previously started actions
* @return int code returned by shell
* @throws \Bitrix\Main\ArgumentTypeException
* @throws \Exception
*/
public function start(array $inputParams = array())
{
if(!is_array($inputParams))
throw new \Bitrix\Main\ArgumentTypeException("inputParams", "array");
if(isset($this->actionParams["MODIFYERS"]) && is_array($this->actionParams["MODIFYERS"]))
{
$needMoreUserInfo = false;
foreach($this->actionParams["MODIFYERS"] as $modifyerFunction)
{
if(is_callable($modifyerFunction))
{
try
{
$this->actionParams = call_user_func($modifyerFunction, $this->id, $this->actionParams, $this->serverHostname, $this->userParams);
}
catch(NeedMoreUserInfoException $e)
{
$this->actionParams = $e->getActionParams();
$needMoreUserInfo = true;
}
}
}
if($needMoreUserInfo)
throw new NeedMoreUserInfoException("Need more user's info", $this->actionParams);
}
$result = null;
$output = '';
$arOutput = array();
$command = $this->makeStartCommand($inputParams);
if($command <> '')
{
$result = $this->shellAdapter->syncExec($command);
$output = $this->shellAdapter->getLastOutput();
$arOutput = array();
if($output <> '')
{
$arOut = json_decode($output, true);
if(is_array($arOut) && !empty($arOut))
$arOutput = $arOut;
}
//error returned by shell
$error = $this->shellAdapter->getLastError();
//error returned by bitrix-env
if(isset($arOutput["error"]) && intval($arOutput["error"]) > 0 && isset($arOutput["message"]) && $arOutput["message"] <> '')
$error .= " ".$arOutput["message"];
$this->makeLogRecords($command, $result, $output, $error);
}
else //$command == ''
{
$result = false;
$error = 'Cant\'t create command for action execution';
}
$this->result = array(
$this->id => array(
"NAME" => isset($this->actionParams["NAME"]) ? $this->actionParams["NAME"] : "[".$this->id."]",
"RESULT" => $result ? "OK" : "ERROR",
"OUTPUT" => array(
"TEXT" => $output,
"DATA" => $arOutput
),
"ERROR" => $error
)
);
return $result;
}
/**
* @return array Last command execution results
*/
public function getResult()
{
return $this->result;
}
protected function makeLogRecords($command = "", $result = null, $output = "", $error = "")
{
if($command <> '')
{
//cut password data from log records
$preg = "/(-p.*\s+|--mysql_password=.*\s+|--cluster_password=.*\s+|--replica_password=.*\s+|--password=.*\s+)/is";
$command = preg_replace($preg, ' PASS_PARAMS ', $command);
$this->log(
($result ? Logger::LOG_LEVEL_INFO : Logger::LOG_LEVEL_ERROR),
"SCALE_ACTION_STARTED",
$this->actionParams["NAME"],
$command
);
}
if($result !== null)
{
$this->log(
($result ? Logger::LOG_LEVEL_INFO : Logger::LOG_LEVEL_ERROR),
"SCALE_ACTION_RESULT",
$this->actionParams["NAME"],
$result ? Loc::getMessage("SCALE_ACTION_RESULT_SUCCESS") : Loc::getMessage("SCALE_ACTION_RESULT_ERROR")
);
}
if($output <> '')
{
$this->log(
Logger::LOG_LEVEL_DEBUG,
"SCALE_ACTION_OUTPUT",
$this->actionParams["NAME"],
$output
);
}
if($error <> '')
{
$this->log(
Logger::LOG_LEVEL_ERROR,
"SCALE_ACTION_ERROR",
$this->actionParams["NAME"],
$error
);
}
}
protected function log($level, $auditType, $actionId, $description)
{
if($this->logLevel < $level)
return false;
return Logger::addRecord($level, $auditType, $actionId, $description);
}
}

View File

@@ -0,0 +1,147 @@
<?
namespace Bitrix\Scale;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class ActionModifyer
* @package Bitrix\Scale
*/
class ActionModifyer
{
/**
* MYSQL_ADD_SLAVE action modifyer
* @param string $actionId - action idenifyer
* @param array $actionParams - action parameterss
* @param string $hostname - server hostname
* @param array $userParamsValues
* @return array - modifyed action params
* @throws NeedMoreUserInfoException
*/
public static function mysqlAddSlave($actionId, $actionParams, $hostname, $userParamsValues)
{
$action = new Action("MYSQL_ADD_SLAVE_MODIFYER", array(
"START_COMMAND_TEMPLATE" => "sudo -u root /opt/webdir/bin/bx-mysql -a options -o json",
"LOG_LEVEL" => Logger::LOG_LEVEL_DISABLE
),
"",
array()
);
$action->start();
$actRes = $action->getResult();
$needModeInfo = false;
if(isset($actRes["MYSQL_ADD_SLAVE_MODIFYER"]["OUTPUT"]["DATA"]["params"]["options"])
&& is_array($actRes["MYSQL_ADD_SLAVE_MODIFYER"]["OUTPUT"]["DATA"]["params"]["options"])
)
{
foreach($actRes["MYSQL_ADD_SLAVE_MODIFYER"]["OUTPUT"]["DATA"]["params"]["options"] as $option)
{
if($option == "cluster_password" || $option == "replica_password")
{
$actionParams["START_COMMAND_TEMPLATE"] .=" --".$option."=".\Bitrix\Scale\Helper::generatePass();
}
elseif($option == "mysql_password")
{
$actionParams["START_COMMAND_TEMPLATE"] .=" --".$option."=##USER_PARAMS:MYSQL_PASS##";
if(!isset($actionParams["USER_PARAMS"]))
$actionParams["USER_PARAMS"] = array();
$actionParams["USER_PARAMS"]["MYSQL_PASS"] = array(
"NAME" => Loc::getMessage("SCALE_AM_MYAR_MYSQL_PASS"),
"TYPE" => "PASSWORD",
"REQUIRED" => "Y",
"VERIFY_TWICE" => "Y"
);
$needModeInfo = true;
}
}
if($needModeInfo)
throw new NeedMoreUserInfoException("Need more user's info", $actionParams);
}
return $actionParams;
}
/**
* MYSQL_ADD_SLAVE, MYSQL_CHANGE_MASTER, MYSQL_DEL_SLAVE actions modifier/
* @param string $actionId - action idenifyer
* @param array $actionParams - action parameters
* @param string $hostname - server hostname
* @param array $userParamsValues
* @return array - modifyed action params
* @throws NeedMoreUserInfoException
*/
public static function checkExtraDbExist($actionId, $actionParams, $hostname, $userParamsValues)
{
if($actionId == "MYSQL_ADD_SLAVE" || $actionId == "MYSQL_CHANGE_MASTER")
$hostname = ServersData::getDbMasterHostname();
if(Helper::isExtraDbExist($hostname))
{
$actionParams["CHECK_EXTRA_DB_USER_ASK"] = "Y";
throw new NeedMoreUserInfoException("Need more user's info", $actionParams);
}
return $actionParams;
}
/**
* SET_EMAIL_SETTINGS modifier
* @param string $actionId
* @param array $actionParams
* @param string $hostname
* @param array $userParamsValues
* @return array mixed
*/
public static function emailSettingsModifier($actionId, $actionParams, $hostname, $userParamsValues)
{
if($actionId != 'SET_EMAIL_SETTINGS')
return $actionParams;
if($userParamsValues['USE_AUTH'] == 'Y')
$pattern = '/(--8<--AUTH_BEGIN----|----AUTH_END--8<--)/';
else
$pattern = '/--8<--AUTH_BEGIN----.*----AUTH_END--8<--/';
$actionParams['START_COMMAND_TEMPLATE'] = preg_replace($pattern, '', $actionParams['START_COMMAND_TEMPLATE']);
return $actionParams;
}
/**
* SITE_CREATE_LINK modifier
* @param string $actionId
* @param array $actionParams
* @param string $hostname
* @param array $userParamsValues
* @return array mixed
*/
public static function siteCreateLinkModifier($actionId, $actionParams, $hostname, $userParamsValues)
{
if($actionId != 'SITE_CREATE_LINK')
return $actionParams;
if(empty($userParamsValues['KERNEL_SITE']))
return $actionParams;
$siteId = $userParamsValues['KERNEL_SITE'];
$sites = SitesData::getList();
if(empty($sites[$siteId]))
return $actionParams;
$actionParams['START_COMMAND_TEMPLATE'] = str_replace(
'##MODIFYER:KERNEL_ROOT##',
$sites[$siteId]['DocumentRoot'],
$actionParams['START_COMMAND_TEMPLATE']
);
return $actionParams;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Bitrix\Scale;
/**
* Class ActionsChain
* @package Bitrix\Scale
*/
class ActionsChain
{
protected $id = "";
protected $userParams = array();
protected $freeParams = array();
protected $actionParams = array();
protected $resultData = array();
protected $serverHostname = "";
public $results = [];
/**
* @param string $actionId
* @param array $actionParams
* @param string $serverHostname
* @param array $userParams
* @param array $freeParams
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Bitrix\Main\ArgumentTypeException
*/
public function __construct($actionId, $actionParams, $serverHostname = "", $userParams = array(), $freeParams = array())
{
if($actionId == '')
throw new \Bitrix\Main\ArgumentNullException("actionId");
if(!is_array($actionParams) || empty($actionParams))
throw new \Exception("Params of action ".$actionId." are not defined correctly!");
if(!isset($actionParams["ACTIONS"]) || !is_array($actionParams["ACTIONS"]))
throw new \Exception("Required param ACTIONS of action ".$actionId." are not defined!");
if(!is_array($userParams))
throw new \Bitrix\Main\ArgumentTypeException("userParams", "array");
if(!is_array($freeParams))
throw new \Bitrix\Main\ArgumentTypeException("freeParams", "array");
$this->id = $actionId;
$this->userParams = $userParams;
$this->freeParams = $freeParams;
$this->actionParams = $actionParams;
$this->serverHostname = $serverHostname;
}
public function getResult()
{
return $this->results;
}
public function getActionObj($actionId)
{
return ActionsData::getActionObject($actionId, $this->serverHostname, $this->userParams, $this->freeParams);
}
public function start($inputParams = array())
{
if(!is_array($inputParams))
throw new \Bitrix\Main\ArgumentTypeException("inputParams", "array");
$result = true;
foreach($this->actionParams["ACTIONS"] as $actionId)
{
$action = $this->getActionObj($actionId);
if(!$action->start($inputParams))
$result = false;
$arRes = $action->getResult();
foreach($arRes as $actId => $res)
$this->results[$actId] = $res;
if(!$result)
break;
if(isset($arRes[$actionId]["OUTPUT"]["DATA"]["params"]) && is_array($arRes[$actionId]["OUTPUT"]["DATA"]["params"]))
foreach($arRes[$actionId]["OUTPUT"]["DATA"]["params"] as $paramId => $paramValue)
if(!isset($inputParams[$paramId]))
$inputParams[$paramId] = $paramValue;
}
return $result;
}
}

View File

@@ -0,0 +1,359 @@
<?php
namespace Bitrix\Scale;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\ArgumentOutOfRangeException;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class ActionsData
* @package Bitrix\Scale
*/
class ActionsData
{
protected static $logLevel = Logger::LOG_LEVEL_INFO;
/**
* @param $actionId
* @return array Action's parameters
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getAction($actionId)
{
if($actionId == '')
throw new \Bitrix\Main\ArgumentNullException("actionId");
$actionsDefinitions = static::getList();
$result = [];
if(isset($actionsDefinitions[$actionId]))
$result = $actionsDefinitions[$actionId];
return $result;
}
/**
* @param string $actionId - action idetifyer
* @param string $serverHostname - server hostname
* @param array $userParams - params filled by user
* @param array $freeParams - params filled somewere in code
* @param array $actionParams - acrion parameters
* @return Action|ActionsChain|bool
* @throws \Bitrix\Main\ArgumentTypeException
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Exception
*/
public static function getActionObject(
$actionId,
$serverHostname = "",
array $userParams = [],
array $freeParams = [],
array $actionParams = []
)
{
if($actionId == '')
throw new \Bitrix\Main\ArgumentNullException("actionId");
if(!is_array($userParams))
throw new \Bitrix\Main\ArgumentTypeException("userParams", "array");
if(!is_array($userParams))
throw new \Bitrix\Main\ArgumentTypeException("freeParams", "array");
if(!is_array($actionParams))
throw new \Bitrix\Main\ArgumentTypeException("actionParams", "array");
$action = false;
if(!isset($actionParams["TYPE"]) || $actionParams["TYPE"] !== "MODIFYED")
$actionParams = static::getAction($actionId);
if(empty($actionParams))
throw new \Exception("Can't find params of action ".$actionId);
if(isset($actionParams["TYPE"]) && $actionParams["TYPE"] === "CHAIN")
$action = new ActionsChain($actionId, $actionParams, $serverHostname, $userParams, $freeParams);
else if(!empty($actionParams))
$action = new Action($actionId, $actionParams, $serverHostname, $userParams, $freeParams);
return $action;
}
/**
* Returns action state
* @param string $bid - action bitrix idetifyer
* @return array
*/
public static function getActionState($bid)
{
$result = [];
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-process -a status -t ".$bid." -o json");
$data = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($data, true);
if(isset($arData["params"][$bid]))
{
$result = $arData["params"][$bid];
}
if($result["status"] === "finished")
{
Logger::addRecord(
Logger::LOG_LEVEL_INFO,
"SCALE_ACTION_CHECK_STATE",
$bid,
Loc::getMessage("SCALE_ACTIONSDATA_ACTION_FINISHED")
);
}
elseif($result["status"] === "error")
{
Logger::addRecord(
Logger::LOG_LEVEL_ERROR,
"SCALE_ACTION_CHECK_STATE",
$bid,
Loc::getMessage("SCALE_ACTIONSDATA_ACTION_ERROR")
);
}
if(self::$logLevel >= Logger::LOG_LEVEL_DEBUG)
{
Logger::addRecord(Logger::LOG_LEVEL_DEBUG, "SCALE_ACTION_CHECK_STATE", $bid, $data);
}
}
return $result;
}
/**
* Returns actions list
* @param bool $checkConditions - if we need to check conditions
* @return array of all actions defenitions
* @throws \Bitrix\Main\IO\FileNotFoundException
*/
public static function getList($checkConditions = false)
{
static $def = null;
if($def == null)
{
$filename = \Bitrix\Main\Application::getDocumentRoot()."/bitrix/modules/scale/include/actionsdefinitions.php";
$file = new \Bitrix\Main\IO\File($filename);
$actionsDefinitions = [];
if($file->isExists())
require_once($filename);
else
throw new \Bitrix\Main\IO\FileNotFoundException($filename);
if(isset($actionsDefinitions))
{
$def = $actionsDefinitions;
if(is_array($def) && $checkConditions)
{
foreach($def as $actionId => $action)
{
if(isset($action["CONDITION"]) && !self::isConditionSatisfied($action["CONDITION"]))
{
unset($def[$actionId]);
}
}
}
if(getenv('BITRIX_ENV_TYPE') === 'crm')
{
unset(
$def['MONITORING_ENABLE'],
$def['SITE_CREATE'],
$def['SITE_CREATE_LINK'],
$def['SITE_CREATE_KERNEL'],
$def['SITE_DEL'],
$def['MEMCACHED_ADD_ROLE'],
$def['MEMCACHED_DEL_ROLE'],
$def['SPHINX_ADD_ROLE'],
$def['PUSH_DEL_ROLE']
);
}
}
else
{
$def = [];
}
}
return $def;
}
/**
* @param array $condition
* @return bool
*/
protected static function isConditionSatisfied($condition): bool
{
$result = true;
if(!isset($condition["COMMAND"], $condition["PARAMS"]) || !is_array($condition["PARAMS"]))
{
return true;
}
if(!isset($condition["PARAMS"][0], $condition["PARAMS"][1], $condition["PARAMS"][2]))
{
return true;
}
$actRes = static::getConditionActionResult($condition["COMMAND"]);
if(isset($actRes["condition"]["OUTPUT"]["DATA"]["params"]))
{
$conditionValue = static::extractConditionValue(
$condition["PARAMS"][0],
$actRes["condition"]["OUTPUT"]["DATA"]["params"]
);
if($conditionValue)
{
$result = static::checkCondition(
$conditionValue,
$condition["PARAMS"][1],
$condition["PARAMS"][2]
);
}
}
return $result;
}
/**
* @param string $paramName
* @param array $paramsValues
* @return string|null
*/
protected static function extractConditionValue(string $paramName, array $paramsValues): ?string
{
$result = null;
$params = explode(":", $paramName);
if(!is_array($params) || count($params) !== 2)
{
throw new ArgumentException('paramName must be like paramSection:paramName');
}
if(isset($paramsValues[$params[0]][$params[1]]))
{
$result = (string)$paramsValues[$params[0]][$params[1]];
}
return $result;
}
/**
* @param string $command
* @return array
*/
protected static function getConditionActionResult(string $command): array
{
$result = [];
try
{
$action = new Action("condition", [
"START_COMMAND_TEMPLATE" => $command,
"LOG_LEVEL" => Logger::LOG_LEVEL_DISABLE
], "", []
);
if($action->start())
{
$result = $action->getResult();
}
}
catch(\Exception $excpt)
{}
return $result;
}
/**
* For data defined in \actionsDefinitions
* @param string $operand1 [CONDITION][PARAMS][0] The real value is obtained from system
* @param string $operator [CONDITION][PARAMS][1] For now it's only "==="
* @param string $operand2 [CONDITION][PARAMS][2]
* @return bool
* @throws ArgumentOutOfRangeException
*/
protected static function checkCondition(string $operand1, string $operator, string $operand2): bool
{
$allowedOperators = ['==='];
if(!in_array($operator, $allowedOperators))
{
throw new ArgumentOutOfRangeException('This "operator" is not allowed');
};
$allowedOperandRegex = '/^[0-9a-zA-Z_:\-\'\"]+$/i';
if(!preg_match($allowedOperandRegex, $operand1))
{
return false;
}
if(!preg_match($allowedOperandRegex, $operand2))
{
throw new ArgumentOutOfRangeException('This "operand2" is wrong');
}
return eval("return ('{$operand1}' {$operator} '{$operand2}');");
}
/**
* @param int $logLevel
*/
public static function setLogLevel($logLevel)
{
self::$logLevel = $logLevel;
}
/**
* Checks if some action is running
* after page refresh, or then smb. else come to page
* during the action running.
* @return array - Action params
*/
public static function checkRunningAction()
{
$result = [];
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-process -a list -o json");
$data = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($data, true);
$result = [];
if(isset($arData["params"]) && is_array($arData["params"]))
{
foreach($arData["params"] as $bid => $actionParams)
{
if(mb_strpos($bid, 'common_') === 0) // || strpos($bid, 'monitor_') === 0)
continue;
if($actionParams["status"] === "running")
{
$result = [$bid => $actionParams];
break;
}
}
}
}
return $result;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Bitrix\Scale;
use \Bitrix\Main\SystemException;
/**
* Exception is thrown when we can't comunicate with the slave server.
*/
class ServerBxInfoException extends SystemException
{
protected $hostname;
public function __construct($message = "", $hostname = "", \Exception $previous = null)
{
parent::__construct($message, 0, '', 0, $previous);
$this->hostname= $hostname;
}
public function getHostname()
{
return $this->hostname;
}
}
/**
* Class NeedMoreUserInfoException
* @package Bitrix\Scale
* If we need more info from user to execute action
*/
class NeedMoreUserInfoException extends SystemException
{
protected $actionParams;
public function __construct($message = "", $actionParams = array(), \Exception $previous = null)
{
parent::__construct($message, 0, '', 0, $previous);
$this->actionParams= $actionParams;
}
public function getActionParams()
{
return $this->actionParams;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Bitrix\Scale;
/**
* Class GraphData
* @package Bitrix\Scale
*/
class GraphData
{
/**
* Returns graphics definition
* @param string $graphCategory
* @return array
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getGraphs($graphCategory)
{
if($graphCategory == '')
throw new \Bitrix\Main\ArgumentNullException("graphCategory");
$graphics = self::getList();
$result = array();
if(isset($graphics[$graphCategory]))
$result = $graphics[$graphCategory];
return $result;
}
/**
* @return array All graphics
* @throws \Bitrix\Main\IO\FileNotFoundException
*/
public static function getList()
{
static $def = null;
if($def == null)
{
$filename = \Bitrix\Main\Application::getDocumentRoot()."/bitrix/modules/scale/include/graphdefinitions.php";
$file = new \Bitrix\Main\IO\File($filename);
if($file->isExists())
require_once($filename);
else
throw new \Bitrix\Main\IO\FileNotFoundException($filename);
if(isset($graphics))
$def = $graphics;
else
$def = array();
}
return $def;
}
}

View File

@@ -0,0 +1,227 @@
<?
namespace Bitrix\Scale;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class Helper
* @package Bitrix\Scale
*/
class Helper
{
const BX_ENV_MIN_VERSION = "7.2.0";
public static function checkBxEnvVersion($version = false)
{
if(!$version)
$version = getenv('BITRIX_VA_VER');
return version_compare($version, self::BX_ENV_MIN_VERSION , '>=');
}
public static function nbsp($str)
{
return str_replace(" ", "&nbsp;",$str);
}
public static function getAvailabilityPage($minutes)
{
if(intval($minutes) <= 0)
throw new \Bitrix\Main\ArgumentNullException("minutes");
$now = time();
$contents = file_get_contents(\Bitrix\Main\Application::getDocumentRoot().'/bitrix/modules/scale/server_off.html');
$contents = str_replace(
"##SITE_NAME##",
\CUtil::JSEscape(\COption::GetOptionString("main","site_name", $_SERVER["SERVER_NAME"])),
$contents
);
$contents = str_replace(
"##CHARSET##",
LANG_CHARSET,
$contents
);
$contents = str_replace(
"##AVAILABLE_MESSAGE##",
Loc::getMessage("SCALE_HLP_AV_MESSAGE"),
$contents
);
$contents = str_replace(
"##AVAILABLE_DATETIME##",
($now+60*$minutes)*1000,
$contents
);
$contents = str_replace(
"##SERVER_NOW##",
$now*1000,
$contents
);
$contents = str_replace(
"##HOURS##",
Loc::getMessage("SCALE_HLP_AV_HOURS")." ",
$contents
);
$contents = str_replace(
"##MINS##",
Loc::getMessage("SCALE_HLP_AV_MINS")." ",
$contents
);
$contents = str_replace(
"##SECS##",
Loc::getMessage("SCALE_HLP_AV_SECS")." ",
$contents
);
return $contents;
}
public static function modifyDbconn($DBHost, $DBName, $DBLogin, $DBPassword)
{
if($DBHost == '')
throw new \Bitrix\Main\ArgumentNullException("DBHost");
if($DBName == '')
throw new \Bitrix\Main\ArgumentNullException("DBName");
if($DBLogin == '')
throw new \Bitrix\Main\ArgumentNullException("DBLogin");
$filename = \Bitrix\Main\Application::getDocumentRoot()."/bitrix/php_interface/dbconn.php";
$file = new \Bitrix\Main\IO\File($filename);
if(!$file->isExists())
return false;
$content = file_get_contents($filename);
if($content == '')
return false;
file_put_contents(\Bitrix\Main\Application::getDocumentRoot()."/bitrix/php_interface/dbconn.php.bak", $content);
$content = preg_replace('/(\$DBHost\s*=\s*(\"|\')+)(.*)((\"|\')+;)/','${1}'.$DBHost.'${4}',$content);
$content = preg_replace('/(\$DBName\s*=\s*(\"|\')+)(.*)((\"|\')+;)/','${1}'.$DBName.'${4}',$content);
$content = preg_replace('/(\$DBLogin\s*=\s*(\"|\')+)(.*)((\"|\')+;)/','${1}'.$DBLogin.'${4}',$content);
$content = preg_replace('/(\$DBPassword\s*=\s*(\"|\')+)(.*)((\"|\')+;)/','${1}'.$DBPassword.'${4}',$content);
return file_put_contents($filename, $content);
}
public static function modifySettings($DBHost, $DBName, $DBLogin, $DBPassword)
{
$filename = $_SERVER['DOCUMENT_ROOT']."/bitrix/.settings-test.php";
if (!file_exists($filename))
return true;
ob_start();
$settings = include($filename);
ob_end_clean();
if (!is_array($settings))
return false;
if(!isset($settings['connections']['value']['default']) || !is_array($settings['connections']['value']['default']))
return true;
$settings['connections']['value']['default']['host'] = $DBHost;
$settings['connections']['value']['default']['database'] = $DBName;
$settings['connections']['value']['default']['login'] = $DBLogin;
$settings['connections']['value']['default']['password'] = $DBPassword;
$data = var_export($settings, true);
rename($filename, $_SERVER['DOCUMENT_ROOT']."/bitrix/.settings-test.php.bak");
file_put_contents($filename, "<"."?php\nreturn ".$data.";\n");
return true;
}
public static function generatePass($length = 20)
{
$chars="abcdefghiknrstyzABCDEFGHKNQRSTYZ1234567890";
$charsCount = mb_strlen($chars);
$result="";
for($i=0; $i<$length; $i++)
$result .= mb_substr($chars, rand(1, $charsCount) - 1, 1);
return $result;
}
public static function isExtraDbExist($hostname)
{
$dbList = ServersData::getDbList($hostname);
$connection = \Bitrix\Main\Application::getConnection();
$currentDb = $connection->getDbName();
$dbCount = count($dbList);
if($dbCount > 1
||($dbCount == 1
&& !in_array($currentDb, $dbList)
)
)
{
$result = true;
}
else
{
$result = false;
}
return $result;
}
public static function getNetworkInterfaces()
{
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-node -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["pool_interfaces"]))
$result = $arData["params"]["pool_interfaces"];
if(is_array($result))
{
foreach($result as $iface => $ip)
$result[$iface] = $iface." (".$ip.")";
}
}
return $result;
}
public static function isScaleCanBeUsed()
{
global $DB;
return getenv('BITRIX_VA_VER')
&& mb_stristr(php_uname('s'), 'linux')
&& $DB->type == 'MYSQL'
&& self::checkBxEnvVersion();
}
public static function getTmpDir()
{
$path = '/home/bitrix/.webdir';
$permissionsForOwnerOnly = 0700;
$res = true;
if(!file_exists($path))
$res = mkdir($path, $permissionsForOwnerOnly, true);
return $res ? $path : '';
}
}

View File

@@ -0,0 +1,62 @@
<?
namespace Bitrix\Scale;
use Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class Logger
* Makes records to bitrix-system site log
* @package Bitrix\Scale
*/
class Logger
{
const LOG_LEVEL_DISABLE = 0;
const LOG_LEVEL_ERROR = 10;
const LOG_LEVEL_INFO = 20;
const LOG_LEVEL_DEBUG = 30;
/**
* @param $level
* @param $auditType
* @param $itemId
* @param $description
* @return bool
*/
public static function addRecord($level, $auditType, $itemId, $description)
{
if($level == self::LOG_LEVEL_ERROR)
$severity = "ERROR";
elseif($level == self::LOG_LEVEL_INFO)
$severity = "INFO";
elseif($level == self::LOG_LEVEL_DEBUG)
$severity = "DEBUG";
else
$severity = "UNKNOWN";
\CEventLog::Add(array(
"SEVERITY" => $severity,
"AUDIT_TYPE_ID" => $auditType,
"MODULE_ID" => "scale",
"ITEM_ID" => $itemId,
"DESCRIPTION" => $description,
));
return true;
}
/**
* @return array
*/
public static function onEventLogGetAuditTypes()
{
return array(
"SCALE_ACTION_STARTED" => "[SCALE_ACTION_STARTED] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_ACTION_STARTED"),
"SCALE_ACTION_RESULT" => "[SCALE_ACTION_RESULT] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_ACTION_RESULT"),
"SCALE_ACTION_ERROR" => "[SCALE_ACTION_ERROR] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_ACTION_ERROR"),
"SCALE_ACTION_OUTPUT" => "[SCALE_ACTION_OUTPUT] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_ACTION_OUTPUT"),
"SCALE_ACTION_CHECK_STATE" => "[SCALE_ACTION_CHECK_STATE] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_ACTION_CHECK_STATE"),
"SCALE_PROVIDER_SEND_ORDER" => "[SCALE_PROVIDER_SEND_ORDER] ".Loc::getMessage("SCALE_ACTION_EVENT_LOG_TYPE_PROVIDER_SEND_ORDER")
);
}
}

View File

@@ -0,0 +1,589 @@
<?php
namespace Bitrix\Scale;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class Monitoring
* @package Bitrix\Scale
*/
class Monitoring
{
protected static $rrdPath = "/var/lib/munin";
protected static $monitoringCategories = array();
/**
* Checks if database files are created
* @return bool
*/
public static function isDatabaseCreated($hostname)
{
$dir = new \Bitrix\Main\IO\Directory(static::$rrdPath."/".$hostname);
return $dir->isExists();
}
/**
* Checks if monitoring is enabled
* @return bool
*/
public static function isEnabled()
{
$result = false;
$command = "sudo -u root /opt/webdir/bin/bx-monitor -o json";
try
{
$action = new Action("is_monitoring_enabled", array(
"START_COMMAND_TEMPLATE" => $command,
"LOG_LEVEL" => Logger::LOG_LEVEL_DISABLE
), "", array());
if(!$action->start())
{
return false;
}
}
catch(\Exception $e)
{
return false;
}
$actRes = $action->getResult();
if(isset($actRes["is_monitoring_enabled"]["OUTPUT"]["DATA"]["params"]["monitor"]["monitoring_status"]))
{
$result = ($actRes["is_monitoring_enabled"]["OUTPUT"]["DATA"]["params"]["monitor"]["monitoring_status"] == "enable");
}
else
{
$result = false;
}
return $result;
}
/**
* Returns value for server role loadbar (thermometr)
* @param $hostname
* @param $roleId
* @return bool|float
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Exception
* @throws \Bitrix\Main\IO\FileNotFoundException
*/
public static function getLoadBarValue($hostname, $roleId)
{
if (!extension_loaded('rrd'))
throw new \Exception("Extension rrd not loaded!");
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
if($roleId == '')
throw new \Bitrix\Main\ArgumentNullException("roleId");
$role = RolesData::getRole($roleId);
if(empty($role))
throw new \Exception("Role with id = ".$roleId." was not defined.");
if(!isset($role["LOADBAR_INFO"]) || $role["LOADBAR_INFO"] == '')
throw new \Exception("Role ".$roleId." has no correctly defined LOADBAR_INFO param .");
$rrdFile = str_replace('##HOSTNAME##', $hostname, $role["LOADBAR_INFO"]);
$rrdPath = "/var/lib/munin/".$hostname."/".$rrdFile;
$file = new \Bitrix\Main\IO\File($rrdPath);
if(!$file->isExists())
throw new \Bitrix\Main\IO\FileNotFoundException($rrdPath);
$data = \rrd_lastupdate($rrdPath);
$result = static::extractRrdValue($data);
return $result;
}
public static function getInfoTableCategory($hostname, $categoryId)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
if($categoryId == '')
throw new \Bitrix\Main\ArgumentNullException("paramId");
$categories = self::getInfoTableCategoriesList($hostname);
$result = array();
if(isset($categories[$categoryId]))
$result = $categories[$categoryId];
return $result;
}
public static function getInfoTableCategoriesList($hostname)
{
$result = array();
$result["HDD"] = array(
"NAME" => Loc::getMessage("SCALE_MONITORING_HDD"),
"PARAMS" => static::getHddsParams($hostname)
);
$result["NET"] = array(
"NAME" => Loc::getMessage("SCALE_MONITORING_NET"),
"PARAMS" => static::getNetParams($hostname)
);
$result["HDDACT"] = array(
"NAME" => Helper::nbsp(Loc::getMessage("SCALE_MONITORING_HDDACT")),
"PARAMS" => static::getHddsUtilization($hostname)
);
$result["MEMORY"] = array(
"NAME" => Loc::getMessage("SCALE_MONITORING_MEMORY"),
"PARAMS" => array(
array(
"NAME" => Loc::getMessage("SCALE_MONITORING_MEMORY_PARAMS"),
"TYPE" => "ARRAY",
"ITEMS" => array(
array(
"VALUE_FUNC" => '\Bitrix\Scale\Monitoring::getMemoryUsage',
"FUNC_PARAMS" => array($hostname)
),
array(
"VALUE_FUNC" => '\Bitrix\Scale\Monitoring::getMemoryUsageValue',
"FUNC_PARAMS" => array($hostname),
"TYPE"=>"LOADBAR"
)
)
)
)
);
$result["AVG_LOAD"] = array(
"NAME" => Helper::nbsp(Loc::getMessage("SCALE_ITS_AVG_LOAD_NAME")),
"PARAMS" => array(
"CURR" => array(
"NAME" => Loc::getMessage("SCALE_ITS_AVG_LOAD_CURR"),
"RRD" => "##HOSTNAME##-load-load-g.rrd",
"CF" => "LAST",
"FORMAT" => "%2.2lf"
),
"MIN" => array(
"NAME" => Loc::getMessage("SCALE_ITS_AVG_LOAD_MIN"),
"RRD" => "##HOSTNAME##-load-load-g.rrd",
"CF" => "MIN",
"FORMAT" => "%2.2lf"
),
"MAX" => array(
"NAME" => Loc::getMessage("SCALE_ITS_AVG_LOAD_MAX"),
"RRD" => "##HOSTNAME##-load-load-g.rrd",
"CF" => "MAX",
"FORMAT" => "%2.2lf"
)
)
);
return $result;
}
public static function getValue($hostname, $categoryId, $param)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
if($categoryId == '')
throw new \Bitrix\Main\ArgumentNullException("categoryId");
if($param == '')
throw new \Bitrix\Main\ArgumentNullException("param");
$arCat = static::getInfoTableCategory($hostname, $categoryId);
if(!$arCat)
throw new \Exception("Monitoring category ".$categoryId." not exist.");
if(!$arCat["PARAMS"][$param])
throw new \Exception("Monitoring param ".$param." in category ".$categoryId." not exist.");
$monParam = $arCat["PARAMS"][$param];
if(isset($monParam["TYPE"]) && $monParam["TYPE"] == "ARRAY")
{
if(!isset($monParam["ITEMS"]) || !is_array($monParam["ITEMS"]))
throw new \Exception("Monitoring param ".$param." in category ".$categoryId." hasn't field ITEMS.");
$result = array();
foreach($monParam["ITEMS"] as $item)
{
$result[] = static::getItemValue($hostname, $categoryId, $item, $param);
}
}
else
{
$result = static::getItemValue($hostname, $categoryId, $monParam, $param);
}
return $result;
}
protected static function getItemValue($hostname, $categoryId, $item, $param)
{
if(isset($item["VALUE"]))
return $item["VALUE"];
if(isset($item["VALUE_FUNC"]))
{
return call_user_func_array($item["VALUE_FUNC"], (isset($item["FUNC_PARAMS"]) ? $item["FUNC_PARAMS"] : array()));
}
if((!$item["RRD"] || !$item["CF"]) && !$item["OPTIONS"])
throw new \Exception("Monitoring param item in category ".$categoryId." has no RRD or CF fields.");
if(isset($item["RRD"]))
{
$rrdFile = str_replace('##HOSTNAME##', $hostname, $item["RRD"]);
$rrdPath = static::$rrdPath."/".$hostname."/".$rrdFile;
$file = new \Bitrix\Main\IO\File($rrdPath);
if(!$file->isExists())
throw new \Bitrix\Main\IO\FileNotFoundException($rrdPath);
$first = \rrd_first($rrdPath);
$last = \rrd_last($rrdPath);
}
if(isset($item["OPTIONS"]))
{
$arOpts = $item["OPTIONS"];
$arOpts = str_replace('##HOSTNAME##', $hostname, $arOpts);
}
else
{
if($item["CF"] == "MIN")
{
$agr = "MINIMUM";
}
elseif($item["CF"] == "MAX")
{
$agr = "MAXIMUM";
}
else
{
$agr = $item["CF"];
}
if($item["CF"] == "LAST")
$item["CF"] = "AVERAGE";
$format = isset($item["FORMAT"]) ? $item["FORMAT"] : "%6.2lf";
$arOpts = array(
"DEF:val=".$rrdPath.":42:".$item["CF"],
"VDEF:vval=val,".$agr,
"PRINT:vval:".$format);
}
if(isset($item["RRD"]))
{
$arOpts[] = "--start";
$arOpts[] = $first;
$arOpts[] = "--end";
$arOpts[] = $last;
}
$data = \rrd_graph( "/dev/null", $arOpts);
if(isset($item["DATA_FUNC"]) && is_callable($item["DATA_FUNC"]))
{
$result = $item["DATA_FUNC"]($data);
}
else
{
if(isset($data["calcpr"]))
{
$data["data"] = $data["calcpr"];
}
$result = static::extractRrdValue($data);
}
return $result;
}
protected static function extractRrdValue($data)
{
$result = false;
if(isset($data["data"]) && is_array($data["data"]))
{
reset($data["data"]);
$result = current($data["data"]);
}
return trim($result);
}
protected static function getAnsibleSetup($hostname)
{
static $info = array();
if(!isset($info[$hostname]))
{
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /usr/bin/ansible ".$hostname." -m setup");
$serversData = $shellAdapter->getLastOutput();
$serversData = mb_substr($serversData, mb_strpos($serversData, "{"));
if($execRes)
{
$info[$hostname] = json_decode($serversData, true);
}
else
{
$info[$hostname] = array();
}
}
return $info[$hostname];
}
protected static function getHddsParams($hostname)
{
$result = array();
$arData = static::getAnsibleSetup($hostname);
if(isset($arData["ansible_facts"]["ansible_mounts"]))
{
foreach($arData["ansible_facts"]["ansible_mounts"] as $mountId => $mount)
{
$result[$mountId] = array(
"NAME" => $mount["device"]." ".Loc::getMessage("SCALE_MONITORING_HDD_PARAMS"),
"TYPE" => "ARRAY",
"ITEMS" => array(
array(
"VALUE_FUNC" => '\Bitrix\Scale\Monitoring::getHddsValues',
"FUNC_PARAMS" => array(
$hostname,
$mountId
)
),
array(
"TYPE" => "LOADBAR",
"VALUE_FUNC" => '\Bitrix\Scale\Monitoring::getHddsUsed',
"FUNC_PARAMS" => array(
$hostname,
$mountId
)
)
)
);
}
}
return $result;
}
protected static function getHddsUsed($hostname, $param)
{
$arData = static::getAnsibleSetup($hostname);
if(isset($arData["ansible_facts"]["ansible_mounts"][$param]["size_total"]) && isset($arData["ansible_facts"]["ansible_mounts"][$param]["size_available"]))
{
$mount = $arData["ansible_facts"]["ansible_mounts"][$param];
$result = ($mount["size_total"]-$mount["size_available"])/$mount["size_total"]*100;
}
else
{
$result = "0";
}
return $result;
}
protected static function getHddsValues($hostname, $param)
{
$arData = static::getAnsibleSetup($hostname);
if(isset($arData["ansible_facts"]["ansible_mounts"][$param]["size_total"]) && isset($arData["ansible_facts"]["ansible_mounts"][$param]["size_available"]))
{
$mount = $arData["ansible_facts"]["ansible_mounts"][$param];
$result = static::formatSize($mount["size_total"], 2)."&nbsp;/&nbsp;".
static::formatSize(($mount["size_total"]-$mount["size_available"]), 2)."&nbsp;/&nbsp;".
static::formatSize($mount["size_available"], 2);
}
else
{
$result = "0";
}
return $result;
}
protected static function getNetParams($hostname)
{
$dir = new \Bitrix\Main\IO\Directory(static::$rrdPath."/".$hostname);
if(!$dir->isExists())
return array();
$arChildren = $dir->getChildren();
$result = array();
foreach ($arChildren as $child)
{
if(!$child->isFile())
continue;
$name = $child->getName();
$pos1 = mb_strpos($name, "-if_");
$pos2 = mb_strpos($name, "-up-");
if($pos1 !== false && $pos2 !== false)
{
$pos1 += 4;
$dev = mb_substr($name, $pos1, $pos2 - $pos1);
$result[$dev] = array(
"NAME" => $dev." ".Loc::getMessage("SCALE_MONITORING_NET_PARAMS"),
"RRD" => $hostname."-if_".$dev."-up-d.rrd",
"OPTIONS" => array(
"DEF:in=".static::$rrdPath."/".$hostname."/".$hostname."-if_".$dev."-up-d.rrd:42:AVERAGE:start=now-600;end=now",
"DEF:out=".static::$rrdPath."/".$hostname."/".$hostname."-if_".$dev."-down-d.rrd:42:AVERAGE:start=now-600;end=now",
"VDEF:vin=in,TOTAL",
"VDEF:vout=out,TOTAL",
"PRINT:vin:%1.2lf",
"PRINT:vout:%1.2lf"
),
"DATA_FUNC" => '\Bitrix\Scale\Monitoring::formatNetParamsValue'
);
}
}
return $result;
}
protected static function formatNetParamsValue(array $data)
{
$result = false;
if(isset($data['calcpr'][0], $data['calcpr'][1]))
{
$result = static::formatSize((int)$data['calcpr'][0]/600) .
'&nbsp;/&nbsp;' .
static::formatSize((int)$data['calcpr'][1]/600).'&nbsp;' .
Helper::nbsp(Loc::getMessage('SCALE_MONITORING_NET_SEC'));
}
return $result;
}
protected static function getHddsUtilization($hostname)
{
$dir = new \Bitrix\Main\IO\Directory(static::$rrdPath."/".$hostname);
if(!$dir->isExists())
return array();
$arChildren = $dir->getChildren();
$result = array();
foreach ($arChildren as $child)
{
if(!$child->isFile())
continue;
$name = $child->getName();
$pos1 = mb_strpos($name, "-diskstats_utilization-");
$pos2 = mb_strpos($name, "-util-");
if($pos1 !== false && $pos2 !== false)
{
$pos1 += 23; //strlen("-diskstats_utilization-")
$dev = mb_substr($name, $pos1, $pos2 - $pos1);
$result[$dev] = array(
"NAME" => $dev." ".Loc::getMessage("SCALE_MONITORING_HDDACT_PARAMS"),
"TYPE" => "ARRAY",
"ITEMS" => array(
array(
"OPTIONS" => array(
"DEF:r=".static::$rrdPath."/".$hostname."/".$hostname."-diskstats_throughput-".$dev."-rdbytes-g.rrd:42:AVERAGE:start=now-600;end=now",
"DEF:w=".static::$rrdPath."/".$hostname."/".$hostname."-diskstats_throughput-".$dev."-wrbytes-g.rrd:42:AVERAGE:start=now-600;end=now",
"VDEF:vr=r,TOTAL",
"VDEF:vw=w,TOTAL",
"PRINT:vr:%1.2lf",
"PRINT:vw:%1.2lf"
),
"DATA_FUNC" => '\Bitrix\Scale\Monitoring::formatHddsUtilizationValue'
),
array(
"RRD" => $hostname."-diskstats_utilization-".$dev."-util-g.rrd",
"CF" => "LAST",
"FORMAT" => "%2.2lf",
"TYPE" => "LOADBAR"
),
)
);
}
}
return $result;
}
protected static function formatHddsUtilizationValue(array $data)
{
$result = false;
if(isset($data['calcpr'][0], $data['calcpr'][1]))
{
$result = static::formatSize((int)$data['calcpr'][0]/600) .
'&nbsp;/&nbsp;' .
static::formatSize((int)$data['calcpr'][1]/600) .
'&nbsp;'.Loc::getMessage('SCALE_MONITORING_NET_SEC');
}
return $result;
}
/**
* @param float $size
* @param int $precision
* @return string
*/
public static function formatSize($size, $precision = 2)
{
static $a = array("b", "Kb", "Mb", "Gb", "Tb");
$pos = 0;
while($size >= 1024 && $pos < 4)
{
$size /= 1024;
$pos++;
}
return round($size, $precision)."&nbsp;".$a[$pos];
}
protected static function getMemoryUsage($hostname)
{
$result = "0";
$arData = static::getAnsibleSetup($hostname);
if(isset($arData["ansible_facts"]["ansible_memtotal_mb"]) && isset($arData["ansible_facts"]["ansible_memfree_mb"]))
$result = $arData["ansible_facts"]["ansible_memtotal_mb"]." / ".(intval($arData["ansible_facts"]["ansible_memtotal_mb"])-intval($arData["ansible_facts"]["ansible_memfree_mb"]))." / ".$arData["ansible_facts"]["ansible_memfree_mb"]." Mb";
return $result;
}
protected static function getMemoryUsageValue($hostname)
{
$result = "0";
$arData = static::getAnsibleSetup($hostname);
if(isset($arData["ansible_facts"]["ansible_memtotal_mb"]) && isset($arData["ansible_facts"]["ansible_memfree_mb"]) && intval($arData["ansible_facts"]["ansible_memtotal_mb"]) != 0)
$result = (intval($arData["ansible_facts"]["ansible_memtotal_mb"]) - intval($arData["ansible_facts"]["ansible_memfree_mb"]))/intval($arData["ansible_facts"]["ansible_memtotal_mb"])*100;
return $result;
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace Bitrix\Scale;
use \Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
use \Bitrix\Main\ArgumentNullException;
/**
* Class Provider
* @package Bitrix\Scale
*/
class Provider {
/**
* @param array $params Params for selection.
* @return array List of available providers.
*/
public static function getList($params = array())
{
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a list -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]) && isset($arData["params"]["providers"]) && is_array($arData["params"]["providers"]))
$result = $arData["params"]["providers"];
if(isset($params["filter"]) && is_array($params["filter"]))
{
foreach($params["filter"] as $filterKey => $filterValue)
{
foreach($result as $providerId => $providerParams)
{
if(!array_key_exists($filterKey, $providerParams) || $providerParams[$filterKey] != $filterValue)
{
unset($result[$providerId]);
}
}
}
}
}
return $result;
}
/**
* @param string $providerId Identifier.
* @return array Status information.
* @throws ArgumentNullException
*/
public static function getStatus($providerId)
{
if($providerId == '' )
throw new ArgumentNullException("providerId");
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a status --provider ".$providerId." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["provider_options"][$providerId]) && is_array($arData["params"]["provider_options"][$providerId]))
$result = $arData["params"]["provider_options"][$providerId];
}
return $result;
}
/**
* @param string $providerId Identifier.
* @return array Avilable configurations.
* @throws ArgumentNullException
*/
public static function getConfigs($providerId)
{
if($providerId == '' )
throw new ArgumentNullException("providerId");
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a configs --provider ".$providerId." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["provider_configs"][$providerId]["configurations"]) && is_array($arData["params"]["provider_configs"][$providerId]["configurations"]))
$result = $arData["params"]["provider_configs"][$providerId]["configurations"];
}
return $result;
}
/**
* @param string $providerId Provider identifier.
* @param string $configId Config idenifier.
* @return int Task identifier.
* @throws ArgumentNullException
*/
public static function sendOrder($providerId, $configId)
{
if($providerId == '' )
throw new ArgumentNullException("providerId");
if($configId == '' )
throw new ArgumentNullException("configId");
$result = "";
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a order --provider ".$providerId." --config_id ".$configId." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["provider_order"][$providerId]["task_id"]))
$result = $arData["params"]["provider_order"][$providerId]["task_id"];
}
if($result <> '')
{
$logLevel = Logger::LOG_LEVEL_INFO;
$description = Loc::getMessage("SCALE_PROVIDER_SEND_ORDER_SUCCESS");
}
else
{
$logLevel = Logger::LOG_LEVEL_ERROR;
$description = Loc::getMessage("SCALE_PROVIDER_SEND_ORDER_ERROR");
}
$description = str_replace(
array("##PROVIDER##", "##CONFIG_ID##", "##ORDER_ID##"),
array($providerId, $configId, $result),
$description
);
Logger::addRecord(
$logLevel,
"SCALE_PROVIDER_SEND_ORDER",
$providerId."::".$configId,
$description);
return $result;
}
/**
* @param string $providerId Provider identifier.
* @param string $taskId Task identifier.
* @return array Status params.
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getOrderStatus($providerId, $taskId)
{
if($providerId == '' )
throw new ArgumentNullException("providerId");
if($taskId == '' )
throw new ArgumentNullException("taskId");
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a order_status --provider ".$providerId." --task_id ".$taskId." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["provider_order"][$providerId]))
$result = $arData["params"]["provider_order"][$providerId];
}
return $result;
}
/**
* @param string $providerId Provider identifier.
* @return array List of orders.
* @throws ArgumentNullException
*/
public static function getOrdersList($providerId = "")
{
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a orders_list".($providerId <> '' ? " --provider ".$providerId : "")." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($jsonData, true);
if(isset($arData["params"]["provider_order_list"]))
$result = $arData["params"]["provider_order_list"];
}
return $result;
}
/**
* Add host from order to pull.
* @param string $providerId Provider identifier.
* @param string $taskId Task identifier.
* @return int
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function addToPullFromOrder($providerId, $taskId)
{
if($providerId == '' )
throw new ArgumentNullException("providerId");
if($taskId == '' )
throw new ArgumentNullException("taskId");
$result = false;
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-provider -a order_to_host --provider ".$providerId." --task_id ".$taskId." -o json");
$jsonData = $shellAdapter->getLastOutput();
if($execRes)
{
$result = json_decode($jsonData, true);
}
return $result;
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Bitrix\Scale;
/**
* Class RolesData
* @package Bitrix\Scale
*/
class RolesData
{
/**
* Returns role defenition
* @param string $roleId
* @return array role params
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getRole($roleId)
{
if($roleId == '')
throw new \Bitrix\Main\ArgumentNullException("roleId");
$rolesDefinitions = self::getList();
$result = array();
if(isset($rolesDefinitions[$roleId]))
$result = $rolesDefinitions[$roleId];
return $result;
}
/**
* @return array All roles defenitions
* @throws \Bitrix\Main\IO\FileNotFoundException
*/
public static function getList()
{
static $def = null;
if($def == null)
{
$filename = \Bitrix\Main\Application::getDocumentRoot()."/bitrix/modules/scale/include/rolesdefinitions.php";
$file = new \Bitrix\Main\IO\File($filename);
if($file->isExists())
require_once($filename);
else
throw new \Bitrix\Main\IO\FileNotFoundException($filename);
if(isset($rolesDefinitions))
$def = $rolesDefinitions;
else
$def = array();
}
return $def;
}
/**
* @param string $roleId
* @return array graphs
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getGraphsCategories($roleId)
{
if($roleId == '')
throw new \Bitrix\Main\ArgumentNullException("roleId");
$result = array();
$role = static::getRole($roleId);
if(isset($role["GRAPH_CATEGORIES"]))
$result = $role["GRAPH_CATEGORIES"];
return $result;
}
}

View File

@@ -0,0 +1,224 @@
<?php
namespace Bitrix\Scale;
/**
* Class ServersData
* @package Bitrix\Scale *
*/
class ServersData
{
protected static $bxInfo = array(); //wrapper_ansible_conf -a bx_info -H hostname
/**
* @param $hostname
* @return array Server's params
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getServer($hostname)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
$result = array();
$servers = self::getList();
if(isset($servers[$hostname]))
$result = $servers[$hostname];
return $result;
}
/**
* @return array List of servers & their params
*/
public static function getList()
{
$result = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/wrapper_ansible_conf -o json");
$serversData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($serversData, true);
//mgmt server must be first
if(isset($arData["params"]) && is_array($arData["params"]))
{
foreach($arData["params"] as $hostname => $server)
{
try
{
$server["BX_ENV_VER"] = static::getBxEnvVer($hostname);
$bxInfo = static::getBxInfo($hostname);
$server["BX_INFO"] = $bxInfo;
if(isset($bxInfo["bx_last_password_change"]))
$server["LAST_PASSWORD_CHANGE"] = $bxInfo["bx_last_password_change"];
if(!$server["BX_ENV_VER"] || !Helper::checkBxEnvVersion($server["BX_ENV_VER"]))
$server["BX_ENV_NEED_UPDATE"] = true;
else
$server["BX_ENV_NEED_UPDATE"] = false;
}
catch(ServerBxInfoException $e)
{
$server["BX_INFO_ERROR"] = $e->getMessage();
}
$result[$hostname] = $server;
}
\sortByColumn($result, array( "host_id" => array(SORT_NUMERIC, SORT_ASC)));
}
}
return $result;
}
/**
* @param $hostname
* @return array Server roles
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getServerRoles($hostname)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
$result = array();
$server = static::getServer($hostname);
if(isset($server["roles"]))
$result = $server["roles"];
$result["SERVER"] = array();
return $result;
}
public static function getDbList($hostname)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
$dbList = array();
$action = new Action("get_db_list", array(
"START_COMMAND_TEMPLATE" => "sudo -u root /opt/webdir/bin/wrapper_ansible_conf -a dbs_list -H ".$hostname." -o json",
"LOG_LEVEL" => Logger::LOG_LEVEL_DISABLE
),
"",
array()
);
$action->start();
$actRes = $action->getResult();
if(isset($actRes["get_db_list"]["OUTPUT"]["DATA"]["params"]["dbs_list"][$hostname]["dbs_list"]))
$dbList = $actRes["get_db_list"]["OUTPUT"]["DATA"]["params"]["dbs_list"][$hostname]["dbs_list"];
if(is_array($dbList))
$result = $dbList;
else
$result = array();
return $result;
}
/**
* @param string $hostname Server hostname.
* @return array server Info.
* @throws \Bitrix\Main\ArgumentNullException
* @throws ServerBxInfoException
*/
protected static function getBxInfo($hostname)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
$result = array();
if(isset(static::$bxInfo[$hostname]))
{
$result = static::$bxInfo[$hostname];
}
else
{
$action = new Action("get_bx_info", array(
"START_COMMAND_TEMPLATE" => "sudo -u root /opt/webdir/bin/wrapper_ansible_conf -a bx_info -H ".$hostname." -o json",
"LOG_LEVEL" => Logger::LOG_LEVEL_DISABLE
),
"",
array()
);
$action->start();
$actRes = $action->getResult();
if(isset($actRes["get_bx_info"]["OUTPUT"]["DATA"]["params"]["bx_variables"][$hostname]))
{
$result = static::$bxInfo[$hostname] = $actRes["get_bx_info"]["OUTPUT"]["DATA"]["params"]["bx_variables"][$hostname];
}
elseif(isset($actRes["get_bx_info"]["RESULT"])
&& $actRes["get_bx_info"]["RESULT"] = "ERROR"
&& $actRes["get_bx_info"]["ERROR"] <> ''
)
{
throw new \Bitrix\Scale\ServerBxInfoException($actRes["get_bx_info"]["ERROR"], $hostname);
}
}
return $result;
}
/**
* @param string $hostname Server hostname.
* @return bool|string - Version of bitrix environment.
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getBxEnvVer($hostname)
{
if($hostname == '')
throw new \Bitrix\Main\ArgumentNullException("hostname");
$bxInfo = static::getBxInfo($hostname);
if(isset($bxInfo["bx_version"])
&& $bxInfo["bx_version"] != "0"
)
{
$result = $bxInfo["bx_version"];
}
else
{
$result = false;
}
return $result;
}
public static function getGraphCategories($hostname)
{
$result = array();
$roles = static::getServerRoles($hostname);
foreach($roles as $roleId => $role)
$result = array_merge($result, \Bitrix\Scale\RolesData::getGraphsCategories($roleId));
return $result;
}
public static function getDbMasterHostname()
{
$servers = static::getList();
foreach($servers as $hostname => $server)
if(isset($server["roles"]["mysql"]["type"]) && $server["roles"]["mysql"]["type"] == "master")
return $hostname;
return false;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Bitrix\Scale;
/**
* Class ShellAdapter
* Executes shell commands
* @package Bitrix\Scale
*/
class ShellAdapter
{
const SUCCESS_RESULT = 0;
protected $resOutput = "";
protected $resError = "";
/**
* Checks and escapes command
* @param string $command
* @return string escapedSring
* @throws \Bitrix\Main\ArgumentNullException
*/
protected function prepareExecution($command)
{
if($command == '')
throw new \Bitrix\Main\ArgumentNullException("command");
$this->resOutput = "";
$this->resError = "";
return escapeshellcmd($command);
}
/**
* Starts execution fo shell command
* Results can be obtained by another special commands
* @param $command
* @return true;
*/
public function asyncExec($command)
{
$outputPath = "/dev/null";
$command = $this->prepareExecution($command);
exec($command. " > ".$outputPath." 2>&1 &");
return true;
}
/**
* @return string Last command output
*/
public function getLastOutput()
{
return $this->resOutput;
}
/**
* @return string last error output
*/
public function getLastError()
{
return $this->resError;
}
/**
* Executes shell command & return shell command execution result
* @param string $command
* @return bool
*/
public function syncExec($command)
{
$command = $this->prepareExecution($command);
$retVal = 1;
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$pipes = array();
$process = proc_open('/bin/bash', $descriptorspec, $pipes);
if (is_resource($process))
{
fwrite($pipes[0], $command);
fclose($pipes[0]);
$this->resOutput = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$this->resError = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$retVal = proc_close($process);
}
return $retVal == static::SUCCESS_RESULT;
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Bitrix\Scale;
use Bitrix\Main\SiteDomainTable;
/**
* Class SitesData
* @package Bitrix\Scale *
*/
class SitesData
{
/**
* @param $siteName
* @return array site's param
* @throws \Bitrix\Main\ArgumentNullException
*/
public static function getSite($siteName, $dbName = false)
{
if($siteName == '')
throw new \Bitrix\Main\ArgumentNullException("siteName");
$result = array();
$sites = self::getList($dbName);
if(isset($sites[$siteName]))
$result = $sites[$siteName];
return $result;
}
/**
* @return string
*/
public static function getKernelSite()
{
foreach(self::getList() as $siteId => $siteParams)
if($siteParams['SiteInstall'] == 'kernel')
return $siteId;
return '';
}
/**
* @return array
*/
public static function getKernelsList()
{
$result = array();
foreach(self::getList() as $siteId => $siteParams)
if($siteParams['SiteInstall'] == 'kernel')
$result[$siteId] = isset($siteParams['NAME']) ? $siteParams['NAME'] : $siteId;
return $result;
}
/**
* @return string
*/
public static function getKernelRoot()
{
foreach(self::getList() as $siteId => $siteParams)
if($siteParams['SiteInstall'] == 'kernel')
return $siteParams['DocumentRoot'];
return '';
}
/**
* @param string $dbName
* @return array List of all sites & their params
*/
public static function getList($dbName = false)
{
static $hitCache = null;
if($hitCache === null)
{
$resSite = array();
$shellAdapter = new ShellAdapter();
$execRes = $shellAdapter->syncExec("sudo -u root /opt/webdir/bin/bx-sites -o json -a list --hiden");
$sitesData = $shellAdapter->getLastOutput();
if($execRes)
{
$arData = json_decode($sitesData, true);
if(isset($arData["params"]))
$resSite = $arData["params"];
$domains = array();
$sdRes = SiteDomainTable::getList();
while($dom = $sdRes->fetch())
{
if(isset($domains[$dom['LID']]))
$domains[$dom['LID']] .= ', ';
else
$domains[$dom['LID']] = '';
$domains[$dom['LID']] .= $dom['DOMAIN'];
}
$rsSite = \Bitrix\Main\SiteTable::getList();
while ($site = $rsSite->fetch())
{
foreach($resSite as $siteId => $siteInfo)
{
$docRoot = $site["DOC_ROOT"] <> '' ? $site["DOC_ROOT"] : \Bitrix\Main\Application::getDocumentRoot();
if($siteInfo["DocumentRoot"] == $docRoot)
{
$resSite[$siteId]["NAME"] = $site["NAME"]." (".$site["LID"].") ";
$resSite[$siteId]["LID"] = $site["LID"];
$resSite[$siteId]["EMAIL"] = $site["EMAIL"];
$resSite[$siteId]["DOMAINS"] = isset($domains[$site["LID"]]) ? $domains[$site["LID"]] : '';
}
else
{
$resSite[$siteId]["NAME"] = $siteId;
}
$resSite[$siteId]["SMTP_USE_AUTH"] = ($siteInfo['SMTPPassword'] !== null && $siteInfo['SMTPUser'] !== null) ? 'Y' : 'N';
}
}
}
$hitCache = $resSite;
}
if($dbName != false && !empty($hitCache))
{
$result = array();
foreach($hitCache as $siteId => $siteInfo)
if($siteInfo['DBName'] == $dbName)
$result[$siteId] = $siteInfo;
}
else
{
$result = $hitCache;
}
return $result;
}
}