1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "remote/createobjecthandler.hpp"
4 #include "remote/configobjectutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/jsonrpcconnection.hpp"
7 #include "remote/filterutility.hpp"
8 #include "remote/apiaction.hpp"
9 #include "remote/zone.hpp"
10 #include "base/configtype.hpp"
11 #include <set>
12
13 using namespace icinga;
14
15 REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler);
16
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)17 bool CreateObjectHandler::HandleRequest(
18 AsioTlsStream& stream,
19 const ApiUser::Ptr& user,
20 boost::beast::http::request<boost::beast::http::string_body>& request,
21 const Url::Ptr& url,
22 boost::beast::http::response<boost::beast::http::string_body>& response,
23 const Dictionary::Ptr& params,
24 boost::asio::yield_context& yc,
25 HttpServerConnection& server
26 )
27 {
28 namespace http = boost::beast::http;
29
30 if (url->GetPath().size() != 4)
31 return false;
32
33 if (request.method() != http::verb::put)
34 return false;
35
36 Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]);
37
38 if (!type) {
39 HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
40 return true;
41 }
42
43 FilterUtility::CheckPermission(user, "objects/create/" + type->GetName());
44
45 String name = url->GetPath()[3];
46 Array::Ptr templates = params->Get("templates");
47 Dictionary::Ptr attrs = params->Get("attrs");
48
49 /* Put created objects into the local zone if not explicitly defined.
50 * This allows additional zone members to sync the
51 * configuration at some later point.
52 */
53 Zone::Ptr localZone = Zone::GetLocalZone();
54 String localZoneName;
55
56 if (localZone) {
57 localZoneName = localZone->GetName();
58
59 if (!attrs) {
60 attrs = new Dictionary({
61 { "zone", localZoneName }
62 });
63 } else if (!attrs->Contains("zone")) {
64 attrs->Set("zone", localZoneName);
65 }
66 }
67
68 /* Sanity checks for unique groups array. */
69 if (attrs->Contains("groups")) {
70 Array::Ptr groups = attrs->Get("groups");
71
72 if (groups)
73 attrs->Set("groups", groups->Unique());
74 }
75
76 Dictionary::Ptr result1 = new Dictionary();
77 String status;
78 Array::Ptr errors = new Array();
79 Array::Ptr diagnosticInformation = new Array();
80
81 bool ignoreOnError = false;
82
83 if (params->Contains("ignore_on_error"))
84 ignoreOnError = HttpUtility::GetLastParameter(params, "ignore_on_error");
85
86 Dictionary::Ptr result = new Dictionary({
87 { "results", new Array({ result1 }) }
88 });
89
90 String config;
91
92 bool verbose = false;
93
94 if (params)
95 verbose = HttpUtility::GetLastParameter(params, "verbose");
96
97 /* Object creation can cause multiple errors and optionally diagnostic information.
98 * We can't use SendJsonError() here.
99 */
100 try {
101 config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs);
102 } catch (const std::exception& ex) {
103 errors->Add(DiagnosticInformation(ex, false));
104 diagnosticInformation->Add(DiagnosticInformation(ex));
105
106 if (verbose)
107 result1->Set("diagnostic_information", diagnosticInformation);
108
109 result1->Set("errors", errors);
110 result1->Set("code", 500);
111 result1->Set("status", "Object could not be created.");
112
113 response.result(http::status::internal_server_error);
114 HttpUtility::SendJsonBody(response, params, result);
115
116 return true;
117 }
118
119 if (!ConfigObjectUtility::CreateObject(type, name, config, errors, diagnosticInformation)) {
120 result1->Set("errors", errors);
121 result1->Set("code", 500);
122 result1->Set("status", "Object could not be created.");
123
124 if (verbose)
125 result1->Set("diagnostic_information", diagnosticInformation);
126
127 response.result(http::status::internal_server_error);
128 HttpUtility::SendJsonBody(response, params, result);
129
130 return true;
131 }
132
133 auto *ctype = dynamic_cast<ConfigType *>(type.get());
134 ConfigObject::Ptr obj = ctype->GetObject(name);
135
136 result1->Set("code", 200);
137
138 if (obj)
139 result1->Set("status", "Object was created");
140 else if (!obj && ignoreOnError)
141 result1->Set("status", "Object was not created but 'ignore_on_error' was set to true");
142
143 response.result(http::status::ok);
144 HttpUtility::SendJsonBody(response, params, result);
145
146 return true;
147 }
148