8af9ff92a650919d438f09c619ac2b5f613eabfb
[public/netxms.git] / src / server / core / container.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: container.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default container class constructor
27 */
28 Container::Container() : NetObj()
29 {
30 m_pdwChildIdList = NULL;
31 m_dwChildIdListSize = 0;
32 m_flags = 0;
33 m_bindFilter = NULL;
34 m_bindFilterSource = NULL;
35 }
36
37 /**
38 * "Normal" container class constructor
39 */
40 Container::Container(const TCHAR *pszName, UINT32 dwCategory) : NetObj()
41 {
42 nx_strncpy(m_name, pszName, MAX_OBJECT_NAME);
43 m_pdwChildIdList = NULL;
44 m_dwChildIdListSize = 0;
45 m_flags = 0;
46 m_bindFilter = NULL;
47 m_bindFilterSource = NULL;
48 m_isHidden = true;
49 }
50
51 /**
52 * Container class destructor
53 */
54 Container::~Container()
55 {
56 safe_free(m_pdwChildIdList);
57 safe_free(m_bindFilterSource);
58 delete m_bindFilter;
59 }
60
61 /**
62 * Create container object from database data.
63 *
64 * @param dwId object ID
65 */
66 bool Container::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
67 {
68 TCHAR szQuery[256];
69 DB_RESULT hResult;
70 UINT32 i;
71
72 m_id = dwId;
73
74 if (!loadCommonProperties(hdb))
75 return false;
76
77 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT auto_bind_filter FROM object_containers WHERE id=%d"), dwId);
78 hResult = DBSelect(hdb, szQuery);
79 if (hResult == NULL)
80 return false; // Query failed
81
82 if (DBGetNumRows(hResult) == 0)
83 {
84 // No object with given ID in database
85 DBFreeResult(hResult);
86 return false;
87 }
88
89 m_bindFilterSource = DBGetField(hResult, 0, 0, NULL, 0);
90 if (m_bindFilterSource != NULL)
91 {
92 TCHAR error[256];
93 m_bindFilter = NXSLCompile(m_bindFilterSource, error, 256, NULL);
94 if (m_bindFilter == NULL)
95 {
96 TCHAR buffer[1024];
97 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
98 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, error, m_id);
99 nxlog_write(MSG_CONTAINER_SCRIPT_COMPILATION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, error);
100 }
101 }
102 DBFreeResult(hResult);
103
104 // Load access list
105 loadACLFromDB(hdb);
106
107 // Load child list for later linkage
108 if (!m_isDeleted)
109 {
110 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT object_id FROM container_members WHERE container_id=%d"), m_id);
111 hResult = DBSelect(hdb, szQuery);
112 if (hResult != NULL)
113 {
114 m_dwChildIdListSize = DBGetNumRows(hResult);
115 if (m_dwChildIdListSize > 0)
116 {
117 m_pdwChildIdList = (UINT32 *)malloc(sizeof(UINT32) * m_dwChildIdListSize);
118 for(i = 0; i < m_dwChildIdListSize; i++)
119 m_pdwChildIdList[i] = DBGetFieldULong(hResult, i, 0);
120 }
121 DBFreeResult(hResult);
122 }
123 }
124
125 return true;
126 }
127
128 /**
129 * Save object to database
130 *
131 * @param hdb database connection handle
132 */
133 bool Container::saveToDatabase(DB_HANDLE hdb)
134 {
135 // Lock object's access
136 lockProperties();
137
138 saveCommonProperties(hdb);
139
140 DB_STATEMENT hStmt;
141 if (IsDatabaseRecordExist(hdb, _T("object_containers"), _T("id"), m_id))
142 {
143 hStmt = DBPrepare(hdb, _T("UPDATE object_containers SET object_class=?,auto_bind_filter=? WHERE id=?"));
144 }
145 else
146 {
147 hStmt = DBPrepare(hdb, _T("INSERT INTO object_containers (object_class,auto_bind_filter,id) VALUES (?,?,?)"));
148 }
149 if (hStmt == NULL)
150 {
151 unlockProperties();
152 return FALSE;
153 }
154
155 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (LONG)getObjectClass());
156 DBBind(hStmt, 2, DB_SQLTYPE_TEXT, m_bindFilterSource, DB_BIND_STATIC);
157 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, m_id);
158 bool success = DBExecute(hStmt);
159 DBFreeStatement(hStmt);
160
161 if (success)
162 {
163 TCHAR query[256];
164
165 // Update members list
166 _sntprintf(query, sizeof(query) / sizeof(TCHAR), _T("DELETE FROM container_members WHERE container_id=%d"), m_id);
167 DBQuery(hdb, query);
168 lockChildList(false);
169 for(int i = 0; i < m_childList->size(); i++)
170 {
171 _sntprintf(query, sizeof(query) / sizeof(TCHAR), _T("INSERT INTO container_members (container_id,object_id) VALUES (%d,%d)"), m_id, m_childList->get(i)->getId());
172 DBQuery(hdb, query);
173 }
174 unlockChildList();
175
176 // Save access list
177 saveACLToDB(hdb);
178
179 // Clear modifications flag and unlock object
180 m_isModified = false;
181 }
182
183 unlockProperties();
184 return success;
185 }
186
187 /**
188 * Delete object from database
189 */
190 bool Container::deleteFromDatabase(DB_HANDLE hdb)
191 {
192 bool success = NetObj::deleteFromDatabase(hdb);
193 if (success)
194 success = executeQueryOnObject(hdb, _T("DELETE FROM object_containers WHERE id=?"));
195 if (success)
196 success = executeQueryOnObject(hdb, _T("DELETE FROM container_members WHERE container_id=?"));
197 return success;
198 }
199
200 /**
201 * Link child objects after loading from database
202 * This method is expected to be called only at startup, so we don't lock
203 */
204 void Container::linkObjects()
205 {
206 NetObj::linkObjects();
207 if (m_dwChildIdListSize > 0)
208 {
209 // Find and link child objects
210 for(UINT32 i = 0; i < m_dwChildIdListSize; i++)
211 {
212 NetObj *pObject = FindObjectById(m_pdwChildIdList[i]);
213 if (pObject != NULL)
214 linkObject(pObject);
215 else
216 nxlog_write(MSG_INVALID_CONTAINER_MEMBER, EVENTLOG_ERROR_TYPE, "dd", m_id, m_pdwChildIdList[i]);
217 }
218
219 // Cleanup
220 free(m_pdwChildIdList);
221 m_pdwChildIdList = NULL;
222 m_dwChildIdListSize = 0;
223 }
224 }
225
226 /**
227 * Calculate status for compound object based on childs' status
228 */
229 void Container::calculateCompoundStatus(BOOL bForcedRecalc)
230 {
231 NetObj::calculateCompoundStatus(bForcedRecalc);
232
233 if ((m_status == STATUS_UNKNOWN) && (m_dwChildIdListSize == 0))
234 {
235 lockProperties();
236 m_status = STATUS_NORMAL;
237 setModified();
238 unlockProperties();
239 }
240 }
241
242 /**
243 * Create NXCP message with object's data
244 */
245 void Container::fillMessageInternal(NXCPMessage *msg)
246 {
247 NetObj::fillMessageInternal(msg);
248 msg->setField(VID_AUTOBIND_FILTER, CHECK_NULL_EX(m_bindFilterSource));
249 }
250
251 /**
252 * Modify object from message
253 */
254 UINT32 Container::modifyFromMessageInternal(NXCPMessage *request)
255 {
256 // Change flags
257 if (request->isFieldExist(VID_FLAGS))
258 m_flags = request->getFieldAsUInt32(VID_FLAGS);
259
260 // Change auto-bind filter
261 if (request->isFieldExist(VID_AUTOBIND_FILTER))
262 {
263 TCHAR *script = request->getFieldAsString(VID_AUTOBIND_FILTER);
264 setAutoBindFilterInternal(script);
265 free(script);
266 }
267
268 return NetObj::modifyFromMessageInternal(request);
269 }
270
271 /**
272 * Set container's autobind script
273 */
274 void Container::setAutoBindFilterInternal(const TCHAR *script)
275 {
276 if (script != NULL)
277 {
278 safe_free(m_bindFilterSource);
279 delete m_bindFilter;
280 m_bindFilterSource = _tcsdup(script);
281 if (m_bindFilterSource != NULL)
282 {
283 TCHAR error[256];
284
285 m_bindFilter = NXSLCompile(m_bindFilterSource, error, 256, NULL);
286 if (m_bindFilter == NULL)
287 {
288 TCHAR buffer[1024];
289 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
290 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, error, m_id);
291 nxlog_write(MSG_CONTAINER_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
292 }
293 }
294 else
295 {
296 m_bindFilter = NULL;
297 }
298 }
299 else
300 {
301 delete_and_null(m_bindFilter);
302 safe_free_and_null(m_bindFilterSource);
303 }
304 setModified();
305 }
306
307 /**
308 * Set auito bind mode for container
309 */
310 void Container::setAutoBindMode(bool doBind, bool doUnbind)
311 {
312 lockProperties();
313
314 if (doBind)
315 m_flags |= CF_AUTO_BIND;
316 else
317 m_flags &= ~CF_AUTO_BIND;
318
319 if (doUnbind)
320 m_flags |= CF_AUTO_UNBIND;
321 else
322 m_flags &= ~CF_AUTO_UNBIND;
323
324 setModified();
325 unlockProperties();
326 }
327
328 /**
329 * Check if object should be placed into container
330 */
331 AutoBindDecision Container::isSuitableForObject(NetObj *object)
332 {
333 AutoBindDecision result = AutoBindDecision_Ignore;
334
335 NXSL_VM *filter = NULL;
336 lockProperties();
337 if ((m_flags & CF_AUTO_BIND) && (m_bindFilter != NULL))
338 {
339 filter = new NXSL_VM(new NXSL_ServerEnv());
340 if (!filter->load(m_bindFilter))
341 {
342 TCHAR buffer[1024];
343 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
344 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
345 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
346 delete_and_null(filter);
347 }
348 }
349 unlockProperties();
350
351 if (filter == NULL)
352 return result;
353
354 filter->setGlobalVariable(_T("$object"), object->createNXSLObject());
355 if (object->getObjectClass() == OBJECT_NODE)
356 filter->setGlobalVariable(_T("$node"), object->createNXSLObject());
357 if (filter->run())
358 {
359 NXSL_Value *value = filter->getResult();
360 if (!value->isNull())
361 result = ((value != NULL) && (value->getValueAsInt32() != 0)) ? AutoBindDecision_Bind : AutoBindDecision_Unbind;
362 }
363 else
364 {
365 lockProperties();
366 TCHAR buffer[1024];
367 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
368 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
369 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
370 unlockProperties();
371 }
372 delete filter;
373 return result;
374 }
375
376 /**
377 * Called by client session handler to check if threshold summary should be shown for this object.
378 */
379 bool Container::showThresholdSummary()
380 {
381 return true;
382 }
383
384 /**
385 * Create NXSL object for this object
386 */
387 NXSL_Value *Container::createNXSLObject()
388 {
389 return new NXSL_Value(new NXSL_Object(&g_nxslContainerClass, this));
390 }
391
392 /**
393 * Serialize object to JSON
394 */
395 json_t *Container::toJson()
396 {
397 json_t *root = NetObj::toJson();
398 json_object_set_new(root, "flags", json_integer(m_flags));
399 json_object_set_new(root, "bindFilter", json_string_t(m_bindFilterSource));
400 return root;
401 }