summaryrefslogtreecommitdiffstats
path: root/library/Director/IcingaConfig/AgentWizard.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Director/IcingaConfig/AgentWizard.php')
-rw-r--r--library/Director/IcingaConfig/AgentWizard.php337
1 files changed, 337 insertions, 0 deletions
diff --git a/library/Director/IcingaConfig/AgentWizard.php b/library/Director/IcingaConfig/AgentWizard.php
new file mode 100644
index 0000000..aceddb1
--- /dev/null
+++ b/library/Director/IcingaConfig/AgentWizard.php
@@ -0,0 +1,337 @@
+<?php
+
+namespace Icinga\Module\Director\IcingaConfig;
+
+use Icinga\Application\Icinga;
+use Icinga\Exception\ProgrammingError;
+use Icinga\Module\Director\Objects\IcingaEndpoint;
+use Icinga\Module\Director\Objects\IcingaHost;
+use Icinga\Module\Director\Objects\IcingaZone;
+use Icinga\Module\Director\Util;
+use LogicException;
+
+class AgentWizard
+{
+ protected $db;
+
+ protected $host;
+
+ protected $parentZone;
+
+ protected $parentEndpoints;
+
+ /** @var string PKI ticket */
+ protected $ticket;
+
+ public function __construct(IcingaHost $host)
+ {
+ $this->host = $host;
+ }
+
+ protected function assertAgent()
+ {
+ if ($this->host->getResolvedProperty('has_agent') !== 'y') {
+ throw new ProgrammingError(
+ 'The given host "%s" is not an Agent',
+ $this->host->getObjectName()
+ );
+ }
+ }
+
+ protected function getCaServer()
+ {
+ return $this->db()->getDeploymentEndpointName();
+
+ // TODO: This is a problem with Icinga 2. Should look like this:
+ // return current($this->getParentEndpoints())->object_name;
+ }
+
+ protected function shouldConnectToMaster()
+ {
+ return $this->host->getResolvedProperty('master_should_connect') !== 'y';
+ }
+
+ protected function getParentZone()
+ {
+ if ($this->parentZone === null) {
+ $this->parentZone = $this->loadParentZone();
+ }
+
+ return $this->parentZone;
+ }
+
+ protected function loadParentZone()
+ {
+ $db = $this->db();
+
+ if ($zoneId = $this->host->getResolvedProperty('zone_id')) {
+ return IcingaZone::loadWithAutoIncId($zoneId, $db);
+ } else {
+ return IcingaZone::load($db->getMasterZoneName(), $db);
+ }
+ }
+
+ protected function getParentEndpoints()
+ {
+ if ($this->parentEndpoints === null) {
+ $this->parentEndpoints = $this->loadParentEndpoints();
+ }
+
+ return $this->parentEndpoints;
+ }
+
+ protected function loadParentEndpoints()
+ {
+ $db = $this->db()->getDbAdapter();
+
+ $query = $db->select()
+ ->from('icinga_endpoint')
+ ->where(
+ 'zone_id = ?',
+ $this->getParentZone()->get('id')
+ );
+
+ return IcingaEndpoint::loadAll(
+ $this->db(),
+ $query,
+ 'object_name'
+ );
+ }
+
+ /**
+ * Get the PKI ticket
+ *
+ * @return string
+ *
+ * @throws LogicException If ticket has not been set
+ */
+ protected function getTicket()
+ {
+ if ($this->ticket === null) {
+ throw new LogicException('Ticket is null');
+ }
+
+ return $this->ticket;
+ }
+
+ /**
+ * Set the PKI ticket
+ *
+ * @param string $ticket
+ *
+ * @return $this
+ */
+ public function setTicket($ticket)
+ {
+ $this->ticket = $ticket;
+ }
+
+ protected function loadPowershellModule()
+ {
+ return $this->getContribFile('windows-agent-installer/Icinga2Agent.psm1');
+ }
+
+ public function renderWindowsInstaller()
+ {
+ return $this->loadPowershellModule()
+ . "\n\n"
+ . 'exit Icinga2AgentModule `' . "\n "
+ . $this->renderPowershellParameters([
+ 'AgentName' => $this->host->getEndpointName(),
+ 'Ticket' => $this->getTicket(),
+ 'ParentZone' => $this->getParentZone()->getObjectName(),
+ 'ParentEndpoints' => array_keys($this->getParentEndpoints()),
+ 'CAServer' => $this->getCaServer(),
+ 'RunInstaller'
+ ]);
+ }
+
+ public function renderIcinga4WindowsWizardCommand($token)
+ {
+ $script = "Use-Icinga;\n"
+ . 'Start-IcingaAgentInstallWizard `' . "\n "
+ . $this->renderPowershellParameters([
+ 'DirectorUrl' => $this->getDirectorUrl(),
+ 'SelfServiceAPIKey' => $token,
+ 'UseDirectorSelfService' => 1,
+ 'OverrideDirectorVars' => 0,
+ 'Reconfigure',
+ 'RunInstaller'
+ ]);
+
+ return $script;
+ }
+
+ public function renderPowershellModuleInstaller($token, $withModule = false)
+ {
+ if ($withModule) {
+ $script = $this->loadPowershellModule() . "\n\n";
+ } else {
+ $script = '';
+ }
+
+ $script .= 'exit Icinga2AgentModule `' . "\n "
+ . $this->renderPowershellParameters([
+ 'DirectorUrl' => $this->getDirectorUrl(),
+ 'DirectorAuthToken' => $token,
+ 'RunInstaller'
+ ]);
+
+ return $script;
+ }
+
+ protected function getDirectorUrl()
+ {
+ $r = Icinga::app()->getRequest();
+ $scheme = $r->getServer('HTTP_X_FORWARDED_PROTO', $r->getScheme());
+
+ return sprintf(
+ '%s://%s%s/director/',
+ $scheme,
+ $r->getHttpHost(),
+ $r->getBaseUrl()
+ );
+ }
+
+ protected function renderPowershellParameters($parameters)
+ {
+ $maxKeyLength = max(array_map('strlen', array_keys($parameters)));
+ foreach ($parameters as $key => $value) {
+ if (is_int($key)) {
+ $maxKeyLength = max($maxKeyLength, strlen($value));
+ }
+ }
+ $parts = array();
+
+ foreach ($parameters as $key => $value) {
+ if (is_int($key)) {
+ $parts[] = $this->renderPowershellParameter($value, null, $maxKeyLength);
+ } else {
+ $parts[] = $this->renderPowershellParameter($key, $value, $maxKeyLength);
+ }
+ }
+
+ return implode(' `' . "\n ", $parts);
+ }
+
+ protected function renderPowershellParameter($key, $value, $maxKeyLength = null)
+ {
+ $ret = '-' . $key;
+ if ($value === null) {
+ return $ret;
+ }
+
+ $ret .= ' ';
+
+ if ($maxKeyLength !== null) {
+ $ret .= str_repeat(' ', $maxKeyLength - strlen($key));
+ }
+
+ if (is_array($value)) {
+ $vals = array();
+ foreach ($value as $val) {
+ $vals[] = $this->renderPowershellString($val);
+ }
+ $ret .= implode(', ', $vals);
+ } elseif (is_int($value)) {
+ $ret .= $value;
+ } else {
+ $ret .= $this->renderPowershellString($value);
+ }
+
+ return $ret;
+ }
+
+ protected function renderPowershellString($string)
+ {
+ // TODO: Escaping
+ return "'" . $string . "'";
+ }
+
+ protected function db()
+ {
+ if ($this->db === null) {
+ $this->db = $this->host->getConnection();
+ }
+
+ return $this->db;
+ }
+
+ public function renderLinuxInstaller()
+ {
+ $script = $this->loadBashModule();
+
+ $endpoints = [];
+ foreach ($this->getParentEndpoints() as $endpoint) {
+ $endpoints[$endpoint->getObjectName()] = $endpoint->get('host');
+ }
+
+ return $this->replaceBashTemplate($script, [
+ 'ICINGA2_NODENAME' => $this->host->getEndpointName(),
+ 'ICINGA2_CA_TICKET' => $this->getTicket(),
+ 'ICINGA2_PARENT_ZONE' => $this->getParentZone()->getObjectName(),
+ 'ICINGA2_PARENT_ENDPOINTS' => $endpoints,
+ 'ICINGA2_CA_NODE' => $this->getCaServer(),
+ 'ICINGA2_GLOBAL_ZONES' => [$this->db()->getDefaultGlobalZoneName()],
+ ]);
+ }
+
+ protected function loadBashModule()
+ {
+ return $this->getContribFile('linux-agent-installer/Icinga2Agent.bash');
+ }
+
+ protected function replaceBashTemplate($script, $parameters)
+ {
+ foreach ($parameters as $key => $value) {
+ $quotedKey = preg_quote($key, '~');
+ if (is_array($value)) {
+ $list = [];
+ foreach ($value as $k => $v) {
+ if (!is_numeric($k)) {
+ $v = "$k,$v";
+ }
+ $list[] = escapeshellarg($v);
+ }
+ $value = '(' . join(' ', $list) . ')';
+ } else {
+ $value = escapeshellarg($value);
+ }
+ $script = preg_replace("~^#?$quotedKey='@$quotedKey@'$~m", "${key}=${value}", $script);
+ }
+
+ return $script;
+ }
+
+ protected function renderBashParameter($key, $value)
+ {
+ $ret = $key . '=';
+
+ // Cheating, this doesn't really help. We should ship the rendered config
+ if (is_array($value) && count($value) === 1) {
+ $value = array_shift($value);
+ }
+
+ if (is_array($value)) {
+ $vals = array();
+ foreach ($value as $val) {
+ $vals[] = $this->renderPowershellString($val);
+ }
+ $ret .= '(' . implode(' ', $vals) . ')';
+ } else {
+ $ret .= $this->renderPowershellString($value);
+ }
+
+ return $ret;
+ }
+
+ protected function getContribDir()
+ {
+ return dirname(dirname(dirname(__DIR__))) . '/contrib';
+ }
+
+ protected function getContribFile($path)
+ {
+ return file_get_contents($this->getContribDir() . '/' . $path);
+ }
+}