1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "remote/eventshandler.hpp"
4 #include "remote/httputility.hpp"
5 #include "remote/filterutility.hpp"
6 #include "config/configcompiler.hpp"
7 #include "config/expression.hpp"
8 #include "base/defer.hpp"
9 #include "base/io-engine.hpp"
10 #include "base/objectlock.hpp"
11 #include "base/json.hpp"
12 #include <boost/asio/buffer.hpp>
13 #include <boost/asio/write.hpp>
14 #include <boost/algorithm/string/replace.hpp>
15 #include <map>
16 #include <set>
17
18 using namespace icinga;
19
20 REGISTER_URLHANDLER("/v1/events", EventsHandler);
21
22 const std::map<String, EventType> l_EventTypes ({
23 {"AcknowledgementCleared", EventType::AcknowledgementCleared},
24 {"AcknowledgementSet", EventType::AcknowledgementSet},
25 {"CheckResult", EventType::CheckResult},
26 {"CommentAdded", EventType::CommentAdded},
27 {"CommentRemoved", EventType::CommentRemoved},
28 {"DowntimeAdded", EventType::DowntimeAdded},
29 {"DowntimeRemoved", EventType::DowntimeRemoved},
30 {"DowntimeStarted", EventType::DowntimeStarted},
31 {"DowntimeTriggered", EventType::DowntimeTriggered},
32 {"Flapping", EventType::Flapping},
33 {"Notification", EventType::Notification},
34 {"StateChange", EventType::StateChange},
35 {"ObjectCreated", EventType::ObjectCreated},
36 {"ObjectDeleted", EventType::ObjectDeleted},
37 {"ObjectModified", EventType::ObjectModified}
38 });
39
40 const String l_ApiQuery ("<API query>");
41
HandleRequest(AsioTlsStream & stream,const ApiUser::Ptr & user,boost::beast::http::request<boost::beast::http::string_body> & request,const Url::Ptr & url,boost::beast::http::response<boost::beast::http::string_body> & response,const Dictionary::Ptr & params,boost::asio::yield_context & yc,HttpServerConnection & server)42 bool EventsHandler::HandleRequest(
43 AsioTlsStream& stream,
44 const ApiUser::Ptr& user,
45 boost::beast::http::request<boost::beast::http::string_body>& request,
46 const Url::Ptr& url,
47 boost::beast::http::response<boost::beast::http::string_body>& response,
48 const Dictionary::Ptr& params,
49 boost::asio::yield_context& yc,
50 HttpServerConnection& server
51 )
52 {
53 namespace asio = boost::asio;
54 namespace http = boost::beast::http;
55
56 if (url->GetPath().size() != 2)
57 return false;
58
59 if (request.method() != http::verb::post)
60 return false;
61
62 if (request.version() == 10) {
63 HttpUtility::SendJsonError(response, params, 400, "HTTP/1.0 not supported for event streams.");
64 return true;
65 }
66
67 Array::Ptr types = params->Get("types");
68
69 if (!types) {
70 HttpUtility::SendJsonError(response, params, 400, "'types' query parameter is required.");
71 return true;
72 }
73
74 {
75 ObjectLock olock(types);
76 for (const String& type : types) {
77 FilterUtility::CheckPermission(user, "events/" + type);
78 }
79 }
80
81 String queueName = HttpUtility::GetLastParameter(params, "queue");
82
83 if (queueName.IsEmpty()) {
84 HttpUtility::SendJsonError(response, params, 400, "'queue' query parameter is required.");
85 return true;
86 }
87
88 std::set<EventType> eventTypes;
89
90 {
91 ObjectLock olock(types);
92 for (const String& type : types) {
93 auto typeId (l_EventTypes.find(type));
94
95 if (typeId != l_EventTypes.end()) {
96 eventTypes.emplace(typeId->second);
97 }
98 }
99 }
100
101 EventsSubscriber subscriber (std::move(eventTypes), HttpUtility::GetLastParameter(params, "filter"), l_ApiQuery);
102
103 server.StartStreaming();
104
105 response.result(http::status::ok);
106 response.set(http::field::content_type, "application/json");
107
108 IoBoundWorkSlot dontLockTheIoThread (yc);
109
110 http::async_write(stream, response, yc);
111 stream.async_flush(yc);
112
113 asio::const_buffer newLine ("\n", 1);
114
115 for (;;) {
116 auto event (subscriber.GetInbox()->Shift(yc));
117
118 if (event) {
119 CpuBoundWork buildingResponse (yc);
120
121 String body = JsonEncode(event);
122
123 boost::algorithm::replace_all(body, "\n", "");
124
125 asio::const_buffer payload (body.CStr(), body.GetLength());
126
127 buildingResponse.Done();
128
129 asio::async_write(stream, payload, yc);
130 asio::async_write(stream, newLine, yc);
131 stream.async_flush(yc);
132 } else if (server.Disconnected()) {
133 return true;
134 }
135 }
136 }
137
138