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