summaryrefslogtreecommitdiffstats
path: root/library/Director/RestApi/IcingaObjectHandler.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Director/RestApi/IcingaObjectHandler.php')
-rw-r--r--library/Director/RestApi/IcingaObjectHandler.php196
1 files changed, 196 insertions, 0 deletions
diff --git a/library/Director/RestApi/IcingaObjectHandler.php b/library/Director/RestApi/IcingaObjectHandler.php
new file mode 100644
index 0000000..7329be3
--- /dev/null
+++ b/library/Director/RestApi/IcingaObjectHandler.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Icinga\Module\Director\RestApi;
+
+use Exception;
+use Icinga\Exception\IcingaException;
+use Icinga\Exception\NotFoundError;
+use Icinga\Exception\ProgrammingError;
+use Icinga\Module\Director\Core\CoreApi;
+use Icinga\Module\Director\Data\Exporter;
+use Icinga\Module\Director\DirectorObject\Lookup\ServiceFinder;
+use Icinga\Module\Director\Exception\DuplicateKeyException;
+use Icinga\Module\Director\Objects\IcingaHost;
+use Icinga\Module\Director\Objects\IcingaObject;
+use Icinga\Module\Director\Resolver\OverrideHelper;
+use InvalidArgumentException;
+use RuntimeException;
+
+class IcingaObjectHandler extends RequestHandler
+{
+ /** @var IcingaObject */
+ protected $object;
+
+ /** @var CoreApi */
+ protected $api;
+
+ public function setObject(IcingaObject $object)
+ {
+ $this->object = $object;
+ return $this;
+ }
+
+ public function setApi(CoreApi $api)
+ {
+ $this->api = $api;
+ return $this;
+ }
+
+ /**
+ * @return IcingaObject
+ * @throws ProgrammingError
+ */
+ protected function requireObject()
+ {
+ if ($this->object === null) {
+ throw new ProgrammingError('Object is required');
+ }
+
+ return $this->object;
+ }
+
+ /**
+ * @return IcingaObject
+ */
+ protected function loadOptionalObject()
+ {
+ return $this->object;
+ }
+
+ protected function requireJsonBody()
+ {
+ $data = json_decode($this->request->getRawBody());
+
+ if ($data === null) {
+ $this->response->setHttpResponseCode(400);
+ throw new IcingaException(
+ 'Invalid JSON: %s',
+ $this->getLastJsonError()
+ );
+ }
+
+ return $data;
+ }
+
+ protected function getType()
+ {
+ return $this->request->getControllerName();
+ }
+
+ protected function processApiRequest()
+ {
+ try {
+ $this->handleApiRequest();
+ } catch (NotFoundError $e) {
+ $this->sendJsonError($e, 404);
+ return;
+ } catch (DuplicateKeyException $e) {
+ $this->sendJsonError($e, 422);
+ return;
+ } catch (Exception $e) {
+ $this->sendJsonError($e);
+ }
+
+ if ($this->request->getActionName() !== 'index') {
+ throw new NotFoundError('Not found');
+ }
+ }
+
+ protected function handleApiRequest()
+ {
+ $request = $this->request;
+ $db = $this->db;
+
+ // TODO: I hate doing this:
+ if ($this->request->getActionName() === 'ticket') {
+ $host = $this->requireObject();
+
+ if ($host->getResolvedProperty('has_agent') !== 'y') {
+ throw new NotFoundError('The host "%s" is not an agent', $host->getObjectName());
+ }
+
+ $this->sendJson($this->api->getTicket($host->getObjectName()));
+
+ // TODO: find a better way to shut down. Currently, this avoids
+ // "not found" errors:
+ exit;
+ }
+
+ switch ($request->getMethod()) {
+ case 'DELETE':
+ $object = $this->requireObject();
+ $object->delete();
+ $this->sendJson($object->toPlainObject(false, true));
+ break;
+
+ case 'POST':
+ case 'PUT':
+ $data = (array) $this->requireJsonBody();
+ $params = $this->request->getUrl()->getParams();
+ $allowsOverrides = $params->get('allowOverrides');
+ $type = $this->getType();
+ if ($object = $this->loadOptionalObject()) {
+ if ($request->getMethod() === 'POST') {
+ $object->setProperties($data);
+ } else {
+ $data = array_merge([
+ 'object_type' => $object->get('object_type'),
+ 'object_name' => $object->getObjectName()
+ ], $data);
+ $object->replaceWith(IcingaObject::createByType($type, $data, $db));
+ }
+ $this->persistChanges($object);
+ $this->sendJson($object->toPlainObject(false, true));
+ } elseif ($allowsOverrides && $type === 'service') {
+ if ($request->getMethod() === 'PUT') {
+ throw new InvalidArgumentException('Overrides are not (yet) available for HTTP PUT');
+ }
+ $this->setServiceProperties($params->getRequired('host'), $params->getRequired('name'), $data);
+ } else {
+ $object = IcingaObject::createByType($type, $data, $db);
+ $this->persistChanges($object);
+ $this->sendJson($object->toPlainObject(false, true));
+ }
+ break;
+
+ case 'GET':
+ $object = $this->requireObject();
+ $exporter = new Exporter($this->db);
+ RestApiParams::applyParamsToExporter($exporter, $this->request, $object->getShortTableName());
+ $this->sendJson($exporter->export($object));
+ break;
+
+ default:
+ $request->getResponse()->setHttpResponseCode(400);
+ throw new IcingaException('Unsupported method ' . $request->getMethod());
+ }
+ }
+
+ protected function persistChanges(IcingaObject $object)
+ {
+ if ($object->hasBeenModified()) {
+ $status = $object->hasBeenLoadedFromDb() ? 200 : 201;
+ $object->store();
+ $this->response->setHttpResponseCode($status);
+ } else {
+ $this->response->setHttpResponseCode(304);
+ }
+ }
+
+ protected function setServiceProperties($hostname, $serviceName, $properties)
+ {
+ $host = IcingaHost::load($hostname, $this->db);
+ $service = ServiceFinder::find($host, $serviceName);
+ if ($service === false) {
+ throw new NotFoundError('Not found');
+ }
+ if ($service->requiresOverrides()) {
+ unset($properties['host']);
+ OverrideHelper::applyOverriddenVars($host, $serviceName, $properties);
+ $this->persistChanges($host);
+ $this->sendJson($host->toPlainObject(false, true));
+ } else {
+ throw new RuntimeException('Found a single service, which should have been found (and dealt with) before');
+ }
+ }
+}