object save optimization - object properties divided into groups and anly modified...
[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 bool success = saveCommonProperties(hdb);
139
140 if (success && (m_modified & MODIFY_OTHER))
141 {
142 DB_STATEMENT hStmt;
143 if (IsDatabaseRecordExist(hdb, _T("object_containers"), _T("id"), m_id))
144 {
145 hStmt = DBPrepare(hdb, _T("UPDATE object_containers SET object_class=?,auto_bind_filter=? WHERE id=?"));
146 }
147 else
148 {
149 hStmt = DBPrepare(hdb, _T("INSERT INTO object_containers (object_class,auto_bind_filter,id) VALUES (?,?,?)"));
150 }
151 if (hStmt == NULL)
152 {
153 unlockProperties();
154 return false;
155 }
156
157 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (LONG)getObjectClass());
158 DBBind(hStmt, 2, DB_SQLTYPE_TEXT, m_bindFilterSource, DB_BIND_STATIC);
159 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, m_id);
160 success = DBExecute(hStmt);
161 DBFreeStatement(hStmt);
162 }
163
164 if (success && (m_modified & MODIFY_RELATIONS))
165 {
166 TCHAR query[256];
167
168 // Update members list
169 _sntprintf(query, sizeof(query) / sizeof(TCHAR), _T("DELETE FROM container_members WHERE container_id=%d"), m_id);
170 DBQuery(hdb, query);
171 lockChildList(false);
172 for(int i = 0; i < m_childList->size(); i++)
173 {
174 _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());
175 DBQuery(hdb, query);
176 }
177 unlockChildList();
178 }
179
180 // Save access list
181 if (success)
182 success = saveACLToDB(hdb);
183
184 // Clear modifications flag and unlock object
185 m_modified = 0;
186 unlockProperties();
187 return success;
188 }
189
190 /**
191 * Delete object from database
192 */
193 bool Container::deleteFromDatabase(DB_HANDLE hdb)
194 {
195 bool success = NetObj::deleteFromDatabase(hdb);
196 if (success)
197 success = executeQueryOnObject(hdb, _T("DELETE FROM object_containers WHERE id=?"));
198 if (success)
199 success = executeQueryOnObject(hdb, _T("DELETE FROM container_members WHERE container_id=?"));
200 return success;
201 }
202
203 /**
204 * Link child objects after loading from database
205 * This method is expected to be called only at startup, so we don't lock
206 */
207 void Container::linkObjects()
208 {
209 NetObj::linkObjects();
210 if (m_dwChildIdListSize > 0)
211 {
212 // Find and link child objects
213 for(UINT32 i = 0; i < m_dwChildIdListSize; i++)
214 {
215 NetObj *pObject = FindObjectById(m_pdwChildIdList[i]);
216 if (pObject != NULL)
217 linkObject(pObject);
218 else
219 nxlog_write(MSG_INVALID_CONTAINER_MEMBER, EVENTLOG_ERROR_TYPE, "dd", m_id, m_pdwChildIdList[i]);
220 }
221
222 // Cleanup
223 free(m_pdwChildIdList);
224 m_pdwChildIdList = NULL;
225 m_dwChildIdListSize = 0;
226 }
227 }
228
229 /**
230 * Calculate status for compound object based on childs' status
231 */
232 void Container::calculateCompoundStatus(BOOL bForcedRecalc)
233 {
234 NetObj::calculateCompoundStatus(bForcedRecalc);
235
236 if ((m_status == STATUS_UNKNOWN) && (m_dwChildIdListSize == 0))
237 {
238 lockProperties();
239 m_status = STATUS_NORMAL;
240 setModified(MODIFY_RUNTIME);
241 unlockProperties();
242 }
243 }
244
245 /**
246 * Create NXCP message with object's data
247 */
248 void Container::fillMessageInternal(NXCPMessage *msg)
249 {
250 NetObj::fillMessageInternal(msg);
251 msg->setField(VID_AUTOBIND_FILTER, CHECK_NULL_EX(m_bindFilterSource));
252 }
253
254 /**
255 * Modify object from message
256 */
257 UINT32 Container::modifyFromMessageInternal(NXCPMessage *request)
258 {
259 // Change flags
260 if (request->isFieldExist(VID_FLAGS))
261 m_flags = request->getFieldAsUInt32(VID_FLAGS);
262
263 // Change auto-bind filter
264 if (request->isFieldExist(VID_AUTOBIND_FILTER))
265 {
266 TCHAR *script = request->getFieldAsString(VID_AUTOBIND_FILTER);
267 setAutoBindFilterInternal(script);
268 free(script);
269 }
270
271 return NetObj::modifyFromMessageInternal(request);
272 }
273
274 /**
275 * Set container's autobind script
276 */
277 void Container::setAutoBindFilterInternal(const TCHAR *script)
278 {
279 if (script != NULL)
280 {
281 free(m_bindFilterSource);
282 delete m_bindFilter;
283 m_bindFilterSource = _tcsdup(script);
284 if (m_bindFilterSource != NULL)
285 {
286 TCHAR error[256];
287
288 m_bindFilter = NXSLCompile(m_bindFilterSource, error, 256, NULL);
289 if (m_bindFilter == NULL)
290 {
291 TCHAR buffer[1024];
292 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
293 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, error, m_id);
294 nxlog_write(MSG_CONTAINER_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
295 }
296 }
297 else
298 {
299 m_bindFilter = NULL;
300 }
301 }
302 else
303 {
304 delete_and_null(m_bindFilter);
305 safe_free_and_null(m_bindFilterSource);
306 }
307 setModified(MODIFY_OTHER);
308 }
309
310 /**
311 * Set auito bind mode for container
312 */
313 void Container::setAutoBindMode(bool doBind, bool doUnbind)
314 {
315 lockProperties();
316
317 if (doBind)
318 m_flags |= CF_AUTO_BIND;
319 else
320 m_flags &= ~CF_AUTO_BIND;
321
322 if (doUnbind)
323 m_flags |= CF_AUTO_UNBIND;
324 else
325 m_flags &= ~CF_AUTO_UNBIND;
326
327 setModified(MODIFY_COMMON_PROPERTIES);
328 unlockProperties();
329 }
330
331 /**
332 * Check if object should be placed into container
333 */
334 AutoBindDecision Container::isSuitableForObject(NetObj *object)
335 {
336 AutoBindDecision result = AutoBindDecision_Ignore;
337
338 NXSL_VM *filter = NULL;
339 lockProperties();
340 if ((m_flags & CF_AUTO_BIND) && (m_bindFilter != NULL))
341 {
342 filter = new NXSL_VM(new NXSL_ServerEnv());
343 if (!filter->load(m_bindFilter))
344 {
345 TCHAR buffer[1024];
346 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
347 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
348 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
349 delete_and_null(filter);
350 }
351 }
352 unlockProperties();
353
354 if (filter == NULL)
355 return result;
356
357 filter->setGlobalVariable(_T("$object"), object->createNXSLObject());
358 if (object->getObjectClass() == OBJECT_NODE)
359 filter->setGlobalVariable(_T("$node"), object->createNXSLObject());
360 if (filter->run())
361 {
362 NXSL_Value *value = filter->getResult();
363 if (!value->isNull())
364 result = ((value != NULL) && (value->getValueAsInt32() != 0)) ? AutoBindDecision_Bind : AutoBindDecision_Unbind;
365 }
366 else
367 {
368 lockProperties();
369 TCHAR buffer[1024];
370 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
371 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
372 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
373 unlockProperties();
374 }
375 delete filter;
376 return result;
377 }
378
379 /**
380 * Called by client session handler to check if threshold summary should be shown for this object.
381 */
382 bool Container::showThresholdSummary()
383 {
384 return true;
385 }
386
387 /**
388 * Create NXSL object for this object
389 */
390 NXSL_Value *Container::createNXSLObject()
391 {
392 return new NXSL_Value(new NXSL_Object(&g_nxslContainerClass, this));
393 }
394
395 /**
396 * Serialize object to JSON
397 */
398 json_t *Container::toJson()
399 {
400 json_t *root = NetObj::toJson();
401 json_object_set_new(root, "flags", json_integer(m_flags));
402 json_object_set_new(root, "bindFilter", json_string_t(m_bindFilterSource));
403 return root;
404 }