diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 52d9f9b8..7e16646e 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Controllers; use Exception; use Icinga\Exception\NotFoundError; +use Icinga\Module\Director\IcingaConfig\AgentWizard; use Icinga\Module\Director\Objects\IcingaEndpoint; use Icinga\Module\Director\Objects\IcingaZone; use Icinga\Module\Director\Util; @@ -75,6 +76,17 @@ class HostController extends ObjectController public function agentAction() { + switch ($this->params->get('download')) { + case 'windows-kickstart': + header('Content-type: application/octet-stream'); + header('Content-Disposition: attachment; filename=icinga2-agent-kickstart.ps1'); + + $wizard = $this->view->wizard = new AgentWizard($this->object); + $wizard->setTicketSalt($this->api()->getTicketSalt()); + echo $wizard->renderWindowsInstaller(); + exit; + } + $this->gracefullyActivateTab('agent'); $this->view->title = 'Agent deployment instructions'; // TODO: Fail when no ticket @@ -86,6 +98,10 @@ class HostController extends ObjectController $this->api()->getTicketSalt() ); + $wizard = $this->view->wizard = new AgentWizard($this->object); + $wizard->setTicketSalt($this->api()->getTicketSalt()); + $this->view->windows = $wizard->renderWindowsInstaller(); + } catch (Exception $e) { $this->view->ticket = 'ERROR'; $this->view->error = sprintf( diff --git a/application/views/scripts/host/agent.phtml b/application/views/scripts/host/agent.phtml index e6149a42..aadba7a1 100644 --- a/application/views/scripts/host/agent.phtml +++ b/application/views/scripts/host/agent.phtml @@ -16,7 +16,28 @@ $master = $this->escape($this->master);
Ticket : = $this->escape($ticket) ?>
+= $this->escape($this->windows) ?> ++
= $this->translate( + 'This requires the Icinga Agent to be installed. It generates and signs' + . ' it\'s certificate and it also generates a minimal icinga2.conf to get' + . ' your agent connected to it\'s parents' +) ?>
Just copy & paste this script (and please scroll down for a corresponding icinga2.cfg):
#!/bin/bash diff --git a/library/Director/IcingaConfig/AgentWizard.php b/library/Director/IcingaConfig/AgentWizard.php new file mode 100644 index 00000000..8f697bb9 --- /dev/null +++ b/library/Director/IcingaConfig/AgentWizard.php @@ -0,0 +1,195 @@ +getResolvedProperty('has_agent') !== 'y') { + throw new ProgrammingError( + 'The given host "%s" is not an Agent', + $host->object_name + ); + } + + $this->host = $host; + } + + protected function getCaServer() + { + return $this->db()->getDeploymentEndpointName(); + + // TODO: This is a problem. Should look like this: + return current($this->getParentEndpoints())->object_name; + } + + protected function shouldConnectToMaster() + { + return $this->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()->id + ); + + return IcingaEndpoint::loadAll( + $this->db(), + $query, + 'object_name' + ); + } + + public function setTicketSalt($salt) + { + $this->salt = $salt; + return $this; + } + + protected function getTicket() + { + return Util::getIcingaTicket( + $this->getCertName(), + $this->getTicketSalt() + ); + } + + protected function getTicketSalt() + { + if ($this->salt === null) { + $this->salt = $this->api()->getTicketSalt(); + } + + return $this->salt; + } + + protected function getCertName() + { + return $this->host->object_name; + } + + protected function loadPowershellModule() + { + return file_get_contents( + dirname(dirname(dirname(__DIR__))) + . '/contrib/windows-agent-installer/Icinga2Agent.psm1' + ); + } + + public function renderWindowsInstaller() + { + return $this->loadPowershellModule() + . "\n\n" + . '$icinga = Icinga2AgentModule `' . "\n " + . $this->renderPowershellParameters( + array( + 'AgentName' => $this->getCertName(), + 'Ticket' => $this->getTicket(), + 'ParentZone' => $this->getParentZone()->object_name, + 'ParentEndpoints' => array_keys($this->getParentEndpoints()), + 'CAServer' => $this->getCaServer(), + ) + ) + . "\n\n" . '$icinga.installIcinga2Agent()' . "\n"; + } + + protected function renderPowershellParameters($parameters) + { + $maxKeyLength = max(array_map('strlen', array_keys($parameters))); + $parts = array(); + + foreach ($parameters as $key => $value) { + $parts[] = $this->renderPowershellParameter($key, $value, $maxKeyLength); + } + + return implode(' `' . "\n ", $parts); + } + + protected function renderPowershellParameter($key, $value, $maxKeyLength = null) + { + $ret = '-' . $key . ' '; + + 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); + } 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; + } +}