b48cce3a07f6098a4a1c9c642a1e954bef3ae49d
[public/netxms.git] / src / server / core / container.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 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 flags,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_flags = DBGetFieldULong(hResult, 0, 0);
90 m_bindFilterSource = DBGetField(hResult, 0, 1, NULL, 0);
91 if (m_bindFilterSource != NULL)
92 {
93 TCHAR error[256];
94 m_bindFilter = NXSLCompile(m_bindFilterSource, error, 256, NULL);
95 if (m_bindFilter == NULL)
96 {
97 TCHAR buffer[1024];
98 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
99 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, error, m_id);
100 nxlog_write(MSG_CONTAINER_SCRIPT_COMPILATION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, error);
101 }
102 }
103 DBFreeResult(hResult);
104
105 // Load access list
106 loadACLFromDB(hdb);
107
108 // Load child list for later linkage
109 if (!m_isDeleted)
110 {
111 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("SELECT object_id FROM container_members WHERE container_id=%d"), m_id);
112 hResult = DBSelect(hdb, szQuery);
113 if (hResult != NULL)
114 {
115 m_dwChildIdListSize = DBGetNumRows(hResult);
116 if (m_dwChildIdListSize > 0)
117 {
118 m_pdwChildIdList = (UINT32 *)malloc(sizeof(UINT32) * m_dwChildIdListSize);
119 for(i = 0; i < m_dwChildIdListSize; i++)
120 m_pdwChildIdList[i] = DBGetFieldULong(hResult, i, 0);
121 }
122 DBFreeResult(hResult);
123 }
124 }
125
126 return true;
127 }
128
129 /**
130 * Save object to database
131 *
132 * @param hdb database connection handle
133 */
134 BOOL Container::saveToDatabase(DB_HANDLE hdb)
135 {
136 // Lock object's access
137 lockProperties();
138
139 saveCommonProperties(hdb);
140
141 DB_STATEMENT hStmt;
142 if (IsDatabaseRecordExist(hdb, _T("object_containers"), _T("id"), m_id))
143 {
144 hStmt = DBPrepare(hdb, _T("UPDATE object_containers SET object_class=?,flags=?,auto_bind_filter=? WHERE id=?"));
145 }
146 else
147 {
148 hStmt = DBPrepare(hdb, _T("INSERT INTO object_containers (object_class,flags,auto_bind_filter,id) VALUES (?,?,?,?)"));
149 }
150 if (hStmt == NULL)
151 {
152 unlockProperties();
153 return FALSE;
154 }
155
156 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (LONG)getObjectClass());
157 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_flags);
158 DBBind(hStmt, 3, DB_SQLTYPE_TEXT, m_bindFilterSource, DB_BIND_STATIC);
159 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_id);
160 BOOL success = DBExecute(hStmt);
161 DBFreeStatement(hStmt);
162
163 if (success)
164 {
165 TCHAR query[256];
166
167 // Update members list
168 _sntprintf(query, sizeof(query) / sizeof(TCHAR), _T("DELETE FROM container_members WHERE container_id=%d"), m_id);
169 DBQuery(hdb, query);
170 lockChildList(false);
171 for(int i = 0; i < m_childList->size(); i++)
172 {
173 _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());
174 DBQuery(hdb, query);
175 }
176 unlockChildList();
177
178 // Save access list
179 saveACLToDB(hdb);
180
181 // Clear modifications flag and unlock object
182 m_isModified = false;
183 }
184
185 unlockProperties();
186 return success;
187 }
188
189 /**
190 * Delete object from database
191 */
192 bool Container::deleteFromDatabase(DB_HANDLE hdb)
193 {
194 bool success = NetObj::deleteFromDatabase(hdb);
195 if (success)
196 success = executeQueryOnObject(hdb, _T("DELETE FROM object_containers WHERE id=?"));
197 if (success)
198 success = executeQueryOnObject(hdb, _T("DELETE FROM container_members WHERE container_id=?"));
199 return success;
200 }
201
202 /**
203 * Link child objects after loading from database
204 * This method is expected to be called only at startup, so we don't lock
205 */
206 void Container::linkChildObjects()
207 {
208 NetObj *pObject;
209 UINT32 i;
210
211 if (m_dwChildIdListSize > 0)
212 {
213 // Find and link child objects
214 for(i = 0; i < m_dwChildIdListSize; i++)
215 {
216 pObject = FindObjectById(m_pdwChildIdList[i]);
217 if (pObject != NULL)
218 linkObject(pObject);
219 else
220 nxlog_write(MSG_INVALID_CONTAINER_MEMBER, EVENTLOG_ERROR_TYPE, "dd", m_id, m_pdwChildIdList[i]);
221 }
222
223 // Cleanup
224 free(m_pdwChildIdList);
225 m_pdwChildIdList = NULL;
226 m_dwChildIdListSize = 0;
227 }
228 }
229
230 /**
231 * Calculate status for compound object based on childs' status
232 */
233 void Container::calculateCompoundStatus(BOOL bForcedRecalc)
234 {
235 NetObj::calculateCompoundStatus(bForcedRecalc);
236
237 if ((m_status == STATUS_UNKNOWN) && (m_dwChildIdListSize == 0))
238 {
239 lockProperties();
240 m_status = STATUS_NORMAL;
241 setModified();
242 unlockProperties();
243 }
244 }
245
246 /**
247 * Create NXCP message with object's data
248 */
249 void Container::fillMessageInternal(NXCPMessage *msg)
250 {
251 NetObj::fillMessageInternal(msg);
252 msg->setField(VID_FLAGS, m_flags);
253 msg->setField(VID_AUTOBIND_FILTER, CHECK_NULL_EX(m_bindFilterSource));
254 }
255
256 /**
257 * Modify object from message
258 */
259 UINT32 Container::modifyFromMessageInternal(NXCPMessage *request)
260 {
261 // Change flags
262 if (request->isFieldExist(VID_FLAGS))
263 m_flags = request->getFieldAsUInt32(VID_FLAGS);
264
265 // Change auto-bind filter
266 if (request->isFieldExist(VID_AUTOBIND_FILTER))
267 {
268 TCHAR *script = request->getFieldAsString(VID_AUTOBIND_FILTER);
269 setAutoBindFilter(script);
270 safe_free(script);
271 }
272
273 return NetObj::modifyFromMessageInternal(request);
274 }
275
276 /**
277 * Set container's autobind script
278 */
279 void Container::setAutoBindFilter(const TCHAR *script)
280 {
281 if (script != NULL)
282 {
283 safe_free(m_bindFilterSource);
284 delete m_bindFilter;
285 m_bindFilterSource = _tcsdup(script);
286 if (m_bindFilterSource != NULL)
287 {
288 TCHAR error[256];
289
290 m_bindFilter = NXSLCompile(m_bindFilterSource, error, 256, NULL);
291 if (m_bindFilter == NULL)
292 {
293 TCHAR buffer[1024];
294 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
295 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, error, m_id);
296 nxlog_write(MSG_CONTAINER_SCRIPT_COMPILATION_ERROR, EVENTLOG_WARNING_TYPE, "dss", m_id, m_name, error);
297 }
298 }
299 else
300 {
301 m_bindFilter = NULL;
302 }
303 }
304 else
305 {
306 delete_and_null(m_bindFilter);
307 safe_free_and_null(m_bindFilterSource);
308 }
309 setModified();
310 }
311
312 /**
313 * Check if node should be placed into container
314 */
315 AutoBindDecision Container::isSuitableForNode(Node *node)
316 {
317 AutoBindDecision result = AutoBindDecision_Ignore;
318
319 NXSL_VM *filter = NULL;
320 lockProperties();
321 if ((m_flags & CF_AUTO_BIND) && (m_bindFilter != NULL))
322 {
323 filter = new NXSL_VM(new NXSL_ServerEnv());
324 if (!filter->load(m_bindFilter))
325 {
326 TCHAR buffer[1024];
327 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
328 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
329 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
330 delete_and_null(filter);
331 }
332 }
333 unlockProperties();
334
335 if (filter == NULL)
336 return result;
337
338 filter->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, node)));
339 if (filter->run())
340 {
341 NXSL_Value *value = filter->getResult();
342 result = ((value != NULL) && (value->getValueAsInt32() != 0)) ? AutoBindDecision_Bind : AutoBindDecision_Unbind;
343 }
344 else
345 {
346 lockProperties();
347 TCHAR buffer[1024];
348 _sntprintf(buffer, 1024, _T("Container::%s::%d"), m_name, m_id);
349 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", buffer, filter->getErrorText(), m_id);
350 nxlog_write(MSG_CONTAINER_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dss", m_id, m_name, filter->getErrorText());
351 unlockProperties();
352 }
353 delete filter;
354 return result;
355 }
356
357 /**
358 * Called by client session handler to check if threshold summary should be shown for this object.
359 */
360 bool Container::showThresholdSummary()
361 {
362 return true;
363 }