From f7ddbaf2d7401fcbc34e07d1b86d673518c4e3f1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 28 Jun 2016 01:55:43 +0200 Subject: [PATCH] host/agent: provide download for Windows Agent --- application/controllers/HostController.php | 16 ++ application/views/scripts/host/agent.phtml | 21 ++ library/Director/IcingaConfig/AgentWizard.php | 195 ++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 library/Director/IcingaConfig/AgentWizard.php 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);

When using the node wizard

Ticket : escape($ticket) ?>

+

Windows Kickstart Script

+qlink( + 'Download', + $this->url()->with('download', 'windows-kickstart'), + null, + array('class' => 'icon-download', 'target' => '_blank') +) ?> +
+escape($this->windows) ?>
+
+

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' +) ?>

Linux commandline

+qlink( + 'Download', + $this->url()->with('download', 'linux'), + null, + array('class' => 'icon-download') +) ?>

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