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); } }