Upgrade wait time changed
[public/netxms.git] / src / server / core / package.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004, 2005 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 ** $module: package.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25
26 //
27 // Check if package with specific parameters already installed
28 //
29
30 BOOL IsPackageInstalled(TCHAR *pszName, TCHAR *pszVersion, TCHAR *pszPlatform)
31 {
32 DB_RESULT hResult;
33 TCHAR szQuery[1024], *pszEscName, *pszEscVersion, *pszEscPlatform;
34 BOOL bResult = FALSE;
35
36 pszEscName = EncodeSQLString(pszName);
37 pszEscVersion = EncodeSQLString(pszVersion);
38 pszEscPlatform = EncodeSQLString(pszPlatform);
39 _sntprintf(szQuery, 1024, _T("SELECT pkg_id FROM agent_pkg WHERE "
40 "pkg_name='%s' AND version='%s' AND platform='%s'"),
41 pszEscName, pszEscVersion, pszEscPlatform);
42 free(pszEscName);
43 free(pszEscVersion);
44 free(pszEscPlatform);
45
46 hResult = DBSelect(g_hCoreDB, szQuery);
47 if (hResult != NULL)
48 {
49 bResult = (DBGetNumRows(hResult) > 0);
50 DBFreeResult(hResult);
51 }
52 return bResult;
53 }
54
55
56 //
57 // Check if given package ID is valid
58 //
59
60 BOOL IsValidPackageId(DWORD dwPkgId)
61 {
62 DB_RESULT hResult;
63 TCHAR szQuery[256];
64 BOOL bResult = FALSE;
65
66 _sntprintf(szQuery, 256, _T("SELECT pkg_name FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
67 hResult = DBSelect(g_hCoreDB, szQuery);
68 if (hResult != NULL)
69 {
70 bResult = (DBGetNumRows(hResult) > 0);
71 DBFreeResult(hResult);
72 }
73 return bResult;
74 }
75
76
77 //
78 // Check if package file with given name exist
79 //
80
81 BOOL IsPackageFileExist(TCHAR *pszFileName)
82 {
83 TCHAR szFullPath[MAX_PATH];
84
85 _tcscpy(szFullPath, g_szDataDir);
86 _tcscat(szFullPath, DDIR_PACKAGES);
87 _tcscat(szFullPath, FS_PATH_SEPARATOR);
88 _tcscat(szFullPath, pszFileName);
89 return (_taccess(szFullPath, 0) == 0);
90 }
91
92
93 //
94 // Uninstall (remove) package from server
95 //
96
97 DWORD UninstallPackage(DWORD dwPkgId)
98 {
99 TCHAR szQuery[256], szFileName[MAX_PATH];
100 DB_RESULT hResult;
101 DWORD dwResult;
102
103 _sntprintf(szQuery, 256, _T("SELECT pkg_file FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
104 hResult = DBSelect(g_hCoreDB, szQuery);
105 if (hResult != NULL)
106 {
107 if (DBGetNumRows(hResult) > 0)
108 {
109 // Delete file from directory
110 _tcscpy(szFileName, g_szDataDir);
111 _tcscat(szFileName, DDIR_PACKAGES);
112 _tcscat(szFileName, FS_PATH_SEPARATOR);
113 _tcscat(szFileName, CHECK_NULL_EX(DBGetField(hResult, 0, 0)));
114 if (_tunlink(szFileName) == 0)
115 {
116 // Delete record from database
117 _sntprintf(szQuery, 256, _T("DELETE FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
118 DBQuery(g_hCoreDB, szQuery);
119 dwResult = RCC_SUCCESS;
120 }
121 else
122 {
123 dwResult = RCC_IO_ERROR;
124 }
125 }
126 else
127 {
128 dwResult = RCC_INVALID_PACKAGE_ID;
129 }
130 DBFreeResult(hResult);
131 }
132 else
133 {
134 dwResult = RCC_DB_FAILURE;
135 }
136 return dwResult;
137 }
138
139
140 //
141 // Package deployment worker thread
142 //
143
144 static THREAD_RESULT THREAD_CALL DeploymentThread(void *pArg)
145 {
146 DT_STARTUP_INFO *pStartup = (DT_STARTUP_INFO *)pArg;
147 Node *pNode;
148 CSCPMessage msg;
149 BOOL bSuccess = FALSE;
150 AgentConnection *pAgentConn;
151 char *pszErrorMsg = "";
152 DWORD dwMaxWait;
153
154 // Read configuration
155 dwMaxWait = ConfigReadULong("AgentUpgradeWaitTime", 600);
156 if (dwMaxWait % 20 != 0)
157 dwMaxWait += 20 - (dwMaxWait % 20);
158
159 // Prepare notification message
160 msg.SetCode(CMD_INSTALLER_INFO);
161 msg.SetId(pStartup->dwRqId);
162
163 while(1)
164 {
165 // Get node object for upgrade
166 pNode = (Node *)pStartup->pQueue->Get();
167 if (pNode == NULL)
168 break; // Queue is empty, exit
169
170 // Preset node id in notification message
171 msg.SetVariable(VID_OBJECT_ID, pNode->Id());
172
173 // Change deployment status to "Initializing"
174 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_INITIALIZE);
175 pStartup->pSession->SendMessage(&msg);
176
177 // Create agent connection
178 pAgentConn = pNode->CreateAgentConnection();
179 if (pAgentConn != NULL)
180 {
181 BOOL bCheckOK = FALSE;
182 char szBuffer[256];
183
184 // Check if package can be deployed on target node
185 if (!stricmp(pStartup->szPlatform, "src"))
186 {
187 // Source package, check if target node
188 // supports source packages
189 if (pAgentConn->GetParameter("Agent.SourcePackageSupport", 32, szBuffer) == ERR_SUCCESS)
190 {
191 bCheckOK = (strtol(szBuffer, NULL, 0) != 0);
192 }
193 }
194 else
195 {
196 // Binary package, check target platform
197 if (pAgentConn->GetParameter("System.PlatformName", 256, szBuffer) == ERR_SUCCESS)
198 {
199 bCheckOK = !stricmp(szBuffer, pStartup->szPlatform);
200 }
201 }
202
203 if (bCheckOK)
204 {
205 // Change deployment status to "File Transfer"
206 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_TRANSFER);
207 pStartup->pSession->SendMessage(&msg);
208
209 // Upload package file to agent
210 strcpy(szBuffer, g_szDataDir);
211 strcat(szBuffer, DDIR_PACKAGES);
212 strcat(szBuffer, FS_PATH_SEPARATOR);
213 strcat(szBuffer, pStartup->szPkgFile);
214 if (pAgentConn->UploadFile(szBuffer) == ERR_SUCCESS)
215 {
216 if (pAgentConn->StartUpgrade(pStartup->szPkgFile) == ERR_SUCCESS)
217 {
218 BOOL bConnected = FALSE;
219 DWORD i;
220
221 // Disconnect from agent
222 pAgentConn->Disconnect();
223
224 // Change deployment status to "Package installation"
225 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_INSTALLATION);
226 pStartup->pSession->SendMessage(&msg);
227
228 // Wait for agent's restart
229 ThreadSleep(20);
230 for(i = 20; i < dwMaxWait; i += 20)
231 {
232 ThreadSleep(20);
233 if (pAgentConn->Connect())
234 {
235 bConnected = TRUE;
236 break; // Connected successfully
237 }
238 }
239
240 // Last attempt to reconnect
241 if (!bConnected)
242 bConnected = pAgentConn->Connect();
243
244 if (bConnected)
245 {
246 // Check version
247 if (pAgentConn->GetParameter("Agent.Version", MAX_AGENT_VERSION_LEN, szBuffer) == ERR_SUCCESS)
248 {
249 if (!stricmp(szBuffer, pStartup->szVersion))
250 {
251 bSuccess = TRUE;
252 }
253 else
254 {
255 pszErrorMsg = "Agent's version doesn't match package version after upgrade";
256 }
257 }
258 else
259 {
260 pszErrorMsg = "Unable to get agent's version after upgrade";
261 }
262 }
263 else
264 {
265 pszErrorMsg = "Unable to contact agent after upgrade";
266 }
267 }
268 else
269 {
270 pszErrorMsg = "Unable to start upgrade process";
271 }
272 }
273 else
274 {
275 pszErrorMsg = "File transfer failed";
276 }
277 }
278 else
279 {
280 pszErrorMsg = "Package is not compatible with target machine";
281 }
282
283 delete pAgentConn;
284 }
285 else
286 {
287 pszErrorMsg = "Unable to connect to agent";
288 }
289
290 // Finish node processing
291 msg.SetVariable(VID_DEPLOYMENT_STATUS,
292 bSuccess ? (WORD)DEPLOYMENT_STATUS_COMPLETED : (WORD)DEPLOYMENT_STATUS_FAILED);
293 msg.SetVariable(VID_ERROR_MESSAGE, pszErrorMsg);
294 pStartup->pSession->SendMessage(&msg);
295 pNode->DecRefCount();
296 }
297 return THREAD_OK;
298 }
299
300
301 //
302 // Package deployment thread
303 //
304
305 THREAD_RESULT THREAD_CALL DeploymentManager(void *pArg)
306 {
307 DT_STARTUP_INFO *pStartup = (DT_STARTUP_INFO *)pArg;
308 DWORD i, dwNumThreads;
309 CSCPMessage msg;
310 Queue *pQueue;
311 THREAD *pThreadList;
312
313 // Wait for parent initialization completion
314 MutexLock(pStartup->mutex, INFINITE);
315 MutexUnlock(pStartup->mutex);
316
317 // Sanity check
318 if (pStartup->dwNumNodes == 0)
319 return THREAD_OK;
320
321 // Read number of upgrade threads
322 dwNumThreads = ConfigReadInt(_T("NumberOfUpgradeThreads"), 10);
323 if (dwNumThreads > pStartup->dwNumNodes)
324 dwNumThreads = pStartup->dwNumNodes;
325
326 // Create processing queue
327 pQueue = new Queue;
328 pStartup->pQueue = pQueue;
329
330 // Send initial status for each node and queue them for deployment
331 msg.SetCode(CMD_INSTALLER_INFO);
332 msg.SetId(pStartup->dwRqId);
333 for(i = 0; i < pStartup->dwNumNodes; i++)
334 {
335 pQueue->Put(pStartup->ppNodeList[i]);
336 msg.SetVariable(VID_OBJECT_ID, pStartup->ppNodeList[i]->Id());
337 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_PENDING);
338 pStartup->pSession->SendMessage(&msg);
339 msg.DeleteAllVariables();
340 }
341
342 // Start worker threads
343 pThreadList = (THREAD *)malloc(sizeof(THREAD) * dwNumThreads);
344 for(i = 0; i < dwNumThreads; i++)
345 pThreadList[i] = ThreadCreateEx(DeploymentThread, 0, pStartup);
346
347 // Wait for all worker threads termination
348 for(i = 0; i < dwNumThreads; i++)
349 ThreadJoin(pThreadList[i]);
350
351 // Send final notification to client
352 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_FINISHED);
353 pStartup->pSession->SendMessage(&msg);
354
355 // Cleanup
356 MutexDestroy(pStartup->mutex);
357 safe_free(pStartup->ppNodeList);
358 free(pStartup);
359 free(pThreadList);
360 delete pQueue;
361
362 return THREAD_OK;
363 }