serverdensity-heroku 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/bin/LICENSE +27 -0
- data/bin/LICENSE-minjson +504 -0
- data/bin/agent-heroku-test.py +2 -0
- data/bin/agent.py +503 -0
- data/bin/checks.py +2497 -0
- data/bin/checks.pyc +0 -0
- data/bin/config.cfg +56 -0
- data/bin/daemon.py +174 -0
- data/bin/daemon.pyc +0 -0
- data/bin/minjson.py +280 -0
- data/bin/plugins.py +315 -0
- data/bin/sd-agent-pkg.init +85 -0
- data/bin/sd-agent.init +73 -0
- data/bin/sd-deploy.py +317 -0
- data/lib/serverdensity-heroku/version.rb +5 -0
- data/lib/serverdensity-heroku.rb +10 -0
- data/serverdensity-heroku.gemspec +19 -0
- metadata +82 -0
data/bin/agent.py
ADDED
@@ -0,0 +1,503 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
'''
|
3
|
+
Server Density
|
4
|
+
www.serverdensity.com
|
5
|
+
----
|
6
|
+
Server monitoring agent for Linux, FreeBSD and Mac OS X
|
7
|
+
|
8
|
+
Licensed under Simplified BSD License (see LICENSE)
|
9
|
+
'''
|
10
|
+
|
11
|
+
import logging
|
12
|
+
|
13
|
+
# General config
|
14
|
+
agentConfig = {}
|
15
|
+
agentConfig['logging'] = logging.DEBUG
|
16
|
+
agentConfig['checkFreq'] = 60
|
17
|
+
|
18
|
+
agentConfig['version'] = '1.11.4'
|
19
|
+
|
20
|
+
rawConfig = {}
|
21
|
+
|
22
|
+
# Check we're not using an old version of Python. Do this before anything else
|
23
|
+
# We need 2.4 above because some modules (like subprocess) were only introduced in 2.4.
|
24
|
+
import sys
|
25
|
+
if int(sys.version_info[1]) <= 3:
|
26
|
+
print 'You are using an outdated version of Python. Please update to v2.4 or above (v3 is not supported). For newer OSs, you can update Python without affecting your system install. See http://blog.boxedice.com/2010/01/19/updating-python-on-rhelcentos/ If you are running RHEl 4 / CentOS 4 then you will need to compile Python manually.'
|
27
|
+
sys.exit(1)
|
28
|
+
|
29
|
+
# Core modules
|
30
|
+
import ConfigParser
|
31
|
+
import os
|
32
|
+
import re
|
33
|
+
import sched
|
34
|
+
import time
|
35
|
+
|
36
|
+
# After the version check as this isn't available on older Python versions
|
37
|
+
# and will error before the message is shown
|
38
|
+
import subprocess
|
39
|
+
|
40
|
+
# Custom modules
|
41
|
+
from checks import checks
|
42
|
+
from daemon import Daemon
|
43
|
+
|
44
|
+
# Config handling
|
45
|
+
try:
|
46
|
+
path = os.path.realpath(__file__)
|
47
|
+
path = os.path.dirname(path)
|
48
|
+
|
49
|
+
config = ConfigParser.ConfigParser()
|
50
|
+
|
51
|
+
if os.path.exists('/etc/sd-agent/config.cfg'):
|
52
|
+
configPath = '/etc/sd-agent/config.cfg'
|
53
|
+
else:
|
54
|
+
configPath = path + '/config.cfg'
|
55
|
+
|
56
|
+
if os.access(configPath, os.R_OK) == False:
|
57
|
+
print 'Unable to read the config file at ' + configPath
|
58
|
+
print 'Agent will now quit'
|
59
|
+
sys.exit(1)
|
60
|
+
|
61
|
+
config.read(configPath)
|
62
|
+
|
63
|
+
# Core config
|
64
|
+
agentConfig['sdUrl'] = config.get('Main', 'sd_url')
|
65
|
+
|
66
|
+
if agentConfig['sdUrl'].endswith('/'):
|
67
|
+
agentConfig['sdUrl'] = agentConfig['sdUrl'][:-1]
|
68
|
+
|
69
|
+
agentConfig['agentKey'] = config.get('Main', 'agent_key')
|
70
|
+
|
71
|
+
# Tmp path
|
72
|
+
if os.path.exists('/var/log/sd-agent/'):
|
73
|
+
agentConfig['tmpDirectory'] = '/var/log/sd-agent/'
|
74
|
+
else:
|
75
|
+
agentConfig['tmpDirectory'] = '/tmp/' # default which may be overriden in the config later
|
76
|
+
|
77
|
+
agentConfig['pidfileDirectory'] = agentConfig['tmpDirectory']
|
78
|
+
|
79
|
+
# Plugin config
|
80
|
+
if config.has_option('Main', 'plugin_directory'):
|
81
|
+
agentConfig['pluginDirectory'] = config.get('Main', 'plugin_directory')
|
82
|
+
|
83
|
+
# Optional config
|
84
|
+
# Also do not need to be present in the config file (case 28326).
|
85
|
+
if config.has_option('Main', 'apache_status_url'):
|
86
|
+
agentConfig['apacheStatusUrl'] = config.get('Main', 'apache_status_url')
|
87
|
+
|
88
|
+
if config.has_option('Main', 'apache_status_user'):
|
89
|
+
agentConfig['apacheStatusUser'] = config.get('Main', 'apache_status_user')
|
90
|
+
|
91
|
+
if config.has_option('Main', 'apache_status_pass'):
|
92
|
+
agentConfig['apacheStatusPass'] = config.get('Main', 'apache_status_pass')
|
93
|
+
|
94
|
+
if config.has_option('Main', 'logging_level'):
|
95
|
+
# Maps log levels from the configuration file to Python log levels
|
96
|
+
loggingLevelMapping = {
|
97
|
+
'debug' : logging.DEBUG,
|
98
|
+
'info' : logging.INFO,
|
99
|
+
'error' : logging.ERROR,
|
100
|
+
'warn' : logging.WARN,
|
101
|
+
'warning' : logging.WARNING,
|
102
|
+
'critical' : logging.CRITICAL,
|
103
|
+
'fatal' : logging.FATAL,
|
104
|
+
}
|
105
|
+
|
106
|
+
customLogging = config.get('Main', 'logging_level')
|
107
|
+
|
108
|
+
try:
|
109
|
+
agentConfig['logging'] = loggingLevelMapping[customLogging.lower()]
|
110
|
+
|
111
|
+
except KeyError, ex:
|
112
|
+
agentConfig['logging'] = logging.INFO
|
113
|
+
|
114
|
+
if config.has_option('Main', 'mongodb_server'):
|
115
|
+
agentConfig['MongoDBServer'] = config.get('Main', 'mongodb_server')
|
116
|
+
|
117
|
+
if config.has_option('Main', 'mongodb_dbstats'):
|
118
|
+
agentConfig['MongoDBDBStats'] = config.get('Main', 'mongodb_dbstats')
|
119
|
+
|
120
|
+
if config.has_option('Main', 'mongodb_replset'):
|
121
|
+
agentConfig['MongoDBReplSet'] = config.get('Main', 'mongodb_replset')
|
122
|
+
|
123
|
+
if config.has_option('Main', 'mysql_server'):
|
124
|
+
agentConfig['MySQLServer'] = config.get('Main', 'mysql_server')
|
125
|
+
|
126
|
+
if config.has_option('Main', 'mysql_user'):
|
127
|
+
agentConfig['MySQLUser'] = config.get('Main', 'mysql_user')
|
128
|
+
|
129
|
+
if config.has_option('Main', 'mysql_pass'):
|
130
|
+
agentConfig['MySQLPass'] = config.get('Main', 'mysql_pass')
|
131
|
+
|
132
|
+
if config.has_option('Main', 'mysql_port'):
|
133
|
+
agentConfig['MySQLPort'] = config.get('Main', 'mysql_port')
|
134
|
+
|
135
|
+
if config.has_option('Main', 'mysql_socket'):
|
136
|
+
agentConfig['MySQLSocket'] = config.get('Main', 'mysql_socket')
|
137
|
+
|
138
|
+
if config.has_option('Main', 'mysql_norepl'):
|
139
|
+
agentConfig['MySQLNoRepl'] = config.get('Main', 'mysql_norepl')
|
140
|
+
|
141
|
+
if config.has_option('Main', 'nginx_status_url'):
|
142
|
+
agentConfig['nginxStatusUrl'] = config.get('Main', 'nginx_status_url')
|
143
|
+
|
144
|
+
if config.has_option('Main', 'tmp_directory'):
|
145
|
+
agentConfig['tmpDirectory'] = config.get('Main', 'tmp_directory')
|
146
|
+
|
147
|
+
if config.has_option('Main', 'pidfile_directory'):
|
148
|
+
agentConfig['pidfileDirectory'] = config.get('Main', 'pidfile_directory')
|
149
|
+
|
150
|
+
if config.has_option('Main', 'rabbitmq_status_url'):
|
151
|
+
agentConfig['rabbitMQStatusUrl'] = config.get('Main', 'rabbitmq_status_url')
|
152
|
+
|
153
|
+
if config.has_option('Main', 'rabbitmq_user'):
|
154
|
+
agentConfig['rabbitMQUser'] = config.get('Main', 'rabbitmq_user')
|
155
|
+
|
156
|
+
if config.has_option('Main', 'rabbitmq_pass'):
|
157
|
+
agentConfig['rabbitMQPass'] = config.get('Main', 'rabbitmq_pass')
|
158
|
+
|
159
|
+
except ConfigParser.NoSectionError, e:
|
160
|
+
print 'Config file not found or incorrectly formatted'
|
161
|
+
print 'Agent will now quit'
|
162
|
+
sys.exit(1)
|
163
|
+
|
164
|
+
except ConfigParser.ParsingError, e:
|
165
|
+
print 'Config file not found or incorrectly formatted'
|
166
|
+
print 'Agent will now quit'
|
167
|
+
sys.exit(1)
|
168
|
+
|
169
|
+
except ConfigParser.NoOptionError, e:
|
170
|
+
print 'There are some items missing from your config file, but nothing fatal'
|
171
|
+
|
172
|
+
# Check to make sure the default config values have been changed (only core config values)
|
173
|
+
if agentConfig['sdUrl'] == 'http://example.serverdensity.com' or agentConfig['agentKey'] == 'keyHere':
|
174
|
+
print 'You have not modified config.cfg for your server'
|
175
|
+
print 'Agent will now quit'
|
176
|
+
sys.exit(1)
|
177
|
+
|
178
|
+
# Check to make sure sd_url is in correct
|
179
|
+
if re.match('http(s)?(\:\/\/)[a-zA-Z0-9_\-]+\.(serverdensity.com)', agentConfig['sdUrl']) == None:
|
180
|
+
print 'Your sd_url is incorrect. It needs to be in the form http://example.serverdensity.com (or using https)'
|
181
|
+
print 'Agent will now quit'
|
182
|
+
sys.exit(1)
|
183
|
+
|
184
|
+
# Check apache_status_url is not empty (case 27073)
|
185
|
+
if 'apacheStatusUrl' in agentConfig and agentConfig['apacheStatusUrl'] == None:
|
186
|
+
print 'You must provide a config value for apache_status_url. If you do not wish to use Apache monitoring, leave it as its default value - http://www.example.com/server-status/?auto'
|
187
|
+
print 'Agent will now quit'
|
188
|
+
sys.exit(1)
|
189
|
+
|
190
|
+
if 'nginxStatusUrl' in agentConfig and agentConfig['nginxStatusUrl'] == None:
|
191
|
+
print 'You must provide a config value for nginx_status_url. If you do not wish to use Nginx monitoring, leave it as its default value - http://www.example.com/nginx_status'
|
192
|
+
print 'Agent will now quit'
|
193
|
+
sys.exit(1)
|
194
|
+
|
195
|
+
if 'MySQLServer' in agentConfig and agentConfig['MySQLServer'] != '' and 'MySQLUser' in agentConfig and agentConfig['MySQLUser'] != '' and 'MySQLPass' in agentConfig:
|
196
|
+
try:
|
197
|
+
import MySQLdb
|
198
|
+
except ImportError:
|
199
|
+
print 'You have configured MySQL for monitoring, but the MySQLdb module is not installed. For more info, see: http://www.serverdensity.com/docs/agent/mysqlstatus/'
|
200
|
+
print 'Agent will now quit'
|
201
|
+
sys.exit(1)
|
202
|
+
|
203
|
+
if 'MongoDBServer' in agentConfig and agentConfig['MongoDBServer'] != '':
|
204
|
+
try:
|
205
|
+
import pymongo
|
206
|
+
except ImportError:
|
207
|
+
print 'You have configured MongoDB for monitoring, but the pymongo module is not installed. For more info, see: http://www.serverdensity.com/docs/agent/mongodbstatus/'
|
208
|
+
print 'Agent will now quit'
|
209
|
+
sys.exit(1)
|
210
|
+
|
211
|
+
for section in config.sections():
|
212
|
+
rawConfig[section] = {}
|
213
|
+
|
214
|
+
for option in config.options(section):
|
215
|
+
rawConfig[section][option] = config.get(section, option)
|
216
|
+
|
217
|
+
# Override the generic daemon class to run our checks
|
218
|
+
class agent(Daemon):
|
219
|
+
|
220
|
+
def run(self):
|
221
|
+
mainLogger.debug('Collecting basic system stats')
|
222
|
+
|
223
|
+
# Get some basic system stats to post back for development/testing
|
224
|
+
import platform
|
225
|
+
systemStats = {'machine': platform.machine(), 'platform': sys.platform, 'processor': platform.processor(), 'pythonV': platform.python_version(), 'cpuCores': self.cpuCores()}
|
226
|
+
|
227
|
+
if sys.platform == 'linux2':
|
228
|
+
systemStats['nixV'] = platform.dist()
|
229
|
+
|
230
|
+
elif sys.platform == 'darwin':
|
231
|
+
systemStats['macV'] = platform.mac_ver()
|
232
|
+
|
233
|
+
elif sys.platform.find('freebsd') != -1:
|
234
|
+
version = platform.uname()[2]
|
235
|
+
systemStats['fbsdV'] = ('freebsd', version, '') # no codename for FreeBSD
|
236
|
+
|
237
|
+
mainLogger.info('System: ' + str(systemStats))
|
238
|
+
|
239
|
+
mainLogger.debug('Creating checks instance')
|
240
|
+
|
241
|
+
# Checks instance
|
242
|
+
c = checks(agentConfig, rawConfig, mainLogger)
|
243
|
+
|
244
|
+
# Schedule the checks
|
245
|
+
mainLogger.info('checkFreq: %s', agentConfig['checkFreq'])
|
246
|
+
s = sched.scheduler(time.time, time.sleep)
|
247
|
+
c.doChecks(s, True, systemStats) # start immediately (case 28315)
|
248
|
+
s.run()
|
249
|
+
|
250
|
+
def cpuCores(self):
|
251
|
+
if sys.platform == 'linux2':
|
252
|
+
grep = subprocess.Popen(['grep', 'model name', '/proc/cpuinfo'], stdout=subprocess.PIPE, close_fds=True)
|
253
|
+
wc = subprocess.Popen(['wc', '-l'], stdin=grep.stdout, stdout=subprocess.PIPE, close_fds=True)
|
254
|
+
output = wc.communicate()[0]
|
255
|
+
return int(output)
|
256
|
+
|
257
|
+
if sys.platform == 'darwin':
|
258
|
+
output = subprocess.Popen(['sysctl', 'hw.ncpu'], stdout=subprocess.PIPE, close_fds=True).communicate()[0].split(': ')[1]
|
259
|
+
return int(output)
|
260
|
+
|
261
|
+
# Control of daemon
|
262
|
+
if __name__ == '__main__':
|
263
|
+
|
264
|
+
# Logging
|
265
|
+
logFile = os.path.join(agentConfig['tmpDirectory'], 'sd-agent.log')
|
266
|
+
|
267
|
+
if os.access(agentConfig['tmpDirectory'], os.W_OK) == False:
|
268
|
+
print 'Unable to write the log file at ' + logFile
|
269
|
+
print 'Agent will now quit'
|
270
|
+
sys.exit(1)
|
271
|
+
|
272
|
+
handler = logging.handlers.RotatingFileHandler(logFile, maxBytes=10485760, backupCount=5) # 10MB files
|
273
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
274
|
+
|
275
|
+
handler.setFormatter(formatter)
|
276
|
+
|
277
|
+
mainLogger = logging.getLogger('main')
|
278
|
+
mainLogger.setLevel(agentConfig['logging'])
|
279
|
+
mainLogger.addHandler(handler)
|
280
|
+
|
281
|
+
mainLogger.info('--')
|
282
|
+
mainLogger.info('sd-agent %s started', agentConfig['version'])
|
283
|
+
mainLogger.info('--')
|
284
|
+
|
285
|
+
mainLogger.info('sd_url: %s', agentConfig['sdUrl'])
|
286
|
+
mainLogger.info('agent_key: %s', agentConfig['agentKey'])
|
287
|
+
|
288
|
+
argLen = len(sys.argv)
|
289
|
+
|
290
|
+
if argLen == 3 or argLen == 4: # needs to accept case when --clean is passed
|
291
|
+
if sys.argv[2] == 'init':
|
292
|
+
# This path added for newer Linux packages which run under
|
293
|
+
# a separate sd-agent user account.
|
294
|
+
if os.path.exists('/var/run/sd-agent/'):
|
295
|
+
pidFile = '/var/run/sd-agent/sd-agent.pid'
|
296
|
+
else:
|
297
|
+
pidFile = '/var/run/sd-agent.pid'
|
298
|
+
|
299
|
+
else:
|
300
|
+
pidFile = os.path.join(agentConfig['pidfileDirectory'], 'sd-agent.pid')
|
301
|
+
|
302
|
+
if os.access(agentConfig['pidfileDirectory'], os.W_OK) == False:
|
303
|
+
print 'Unable to write the PID file at ' + pidFile
|
304
|
+
print 'Agent will now quit'
|
305
|
+
sys.exit(1)
|
306
|
+
|
307
|
+
mainLogger.info('PID: %s', pidFile)
|
308
|
+
|
309
|
+
if argLen == 4 and sys.argv[3] == '--clean':
|
310
|
+
mainLogger.info('--clean')
|
311
|
+
try:
|
312
|
+
os.remove(pidFile)
|
313
|
+
except OSError:
|
314
|
+
# Did not find pid file
|
315
|
+
pass
|
316
|
+
|
317
|
+
# Daemon instance from agent class
|
318
|
+
daemon = agent(pidFile)
|
319
|
+
|
320
|
+
# Control options
|
321
|
+
if argLen == 2 or argLen == 3 or argLen == 4:
|
322
|
+
if 'start' == sys.argv[1]:
|
323
|
+
mainLogger.info('Action: start')
|
324
|
+
daemon.start()
|
325
|
+
|
326
|
+
elif 'stop' == sys.argv[1]:
|
327
|
+
mainLogger.info('Action: stop')
|
328
|
+
daemon.stop()
|
329
|
+
|
330
|
+
elif 'restart' == sys.argv[1]:
|
331
|
+
mainLogger.info('Action: restart')
|
332
|
+
daemon.restart()
|
333
|
+
|
334
|
+
elif 'foreground' == sys.argv[1]:
|
335
|
+
mainLogger.info('Action: foreground')
|
336
|
+
daemon.run()
|
337
|
+
|
338
|
+
elif 'status' == sys.argv[1]:
|
339
|
+
mainLogger.info('Action: status')
|
340
|
+
|
341
|
+
try:
|
342
|
+
pf = file(pidFile,'r')
|
343
|
+
pid = int(pf.read().strip())
|
344
|
+
pf.close()
|
345
|
+
except IOError:
|
346
|
+
pid = None
|
347
|
+
except SystemExit:
|
348
|
+
pid = None
|
349
|
+
|
350
|
+
if pid:
|
351
|
+
print 'sd-agent is running as pid %s.' % pid
|
352
|
+
else:
|
353
|
+
print 'sd-agent is not running.'
|
354
|
+
|
355
|
+
elif 'update' == sys.argv[1]:
|
356
|
+
mainLogger.info('Action: update')
|
357
|
+
|
358
|
+
if os.path.abspath(__file__) == '/usr/bin/sd-agent/agent.py':
|
359
|
+
print 'Please use the Linux package manager that was used to install the agent to update it.'
|
360
|
+
print 'e.g. yum install sd-agent or apt-get install sd-agent'
|
361
|
+
sys.exit(1)
|
362
|
+
|
363
|
+
import httplib
|
364
|
+
import platform
|
365
|
+
import urllib2
|
366
|
+
|
367
|
+
print 'Checking if there is a new version';
|
368
|
+
|
369
|
+
# Get the latest version info
|
370
|
+
try:
|
371
|
+
mainLogger.debug('Update: checking for update')
|
372
|
+
|
373
|
+
request = urllib2.urlopen('http://www.serverdensity.com/agentupdate/')
|
374
|
+
response = request.read()
|
375
|
+
|
376
|
+
except urllib2.HTTPError, e:
|
377
|
+
print 'Unable to get latest version info - HTTPError = ' + str(e)
|
378
|
+
sys.exit(1)
|
379
|
+
|
380
|
+
except urllib2.URLError, e:
|
381
|
+
print 'Unable to get latest version info - URLError = ' + str(e)
|
382
|
+
sys.exit(1)
|
383
|
+
|
384
|
+
except httplib.HTTPException, e:
|
385
|
+
print 'Unable to get latest version info - HTTPException'
|
386
|
+
sys.exit(1)
|
387
|
+
|
388
|
+
except Exception, e:
|
389
|
+
import traceback
|
390
|
+
print 'Unable to get latest version info - Exception = ' + traceback.format_exc()
|
391
|
+
sys.exit(1)
|
392
|
+
|
393
|
+
mainLogger.debug('Update: importing json/minjson')
|
394
|
+
|
395
|
+
# We need to return the data using JSON. As of Python 2.6+, there is a core JSON
|
396
|
+
# module. We have a 2.4/2.5 compatible lib included with the agent but if we're
|
397
|
+
# on 2.6 or above, we should use the core module which will be faster
|
398
|
+
pythonVersion = platform.python_version_tuple()
|
399
|
+
|
400
|
+
# Decode the JSON
|
401
|
+
if int(pythonVersion[1]) >= 6: # Don't bother checking major version since we only support v2 anyway
|
402
|
+
import json
|
403
|
+
|
404
|
+
mainLogger.debug('Update: decoding JSON (json)')
|
405
|
+
|
406
|
+
try:
|
407
|
+
updateInfo = json.loads(response)
|
408
|
+
except Exception, e:
|
409
|
+
print 'Unable to get latest version info. Try again later.'
|
410
|
+
sys.exit(1)
|
411
|
+
|
412
|
+
else:
|
413
|
+
import minjson
|
414
|
+
|
415
|
+
mainLogger.debug('Update: decoding JSON (minjson)')
|
416
|
+
|
417
|
+
try:
|
418
|
+
updateInfo = minjson.safeRead(response)
|
419
|
+
except Exception, e:
|
420
|
+
print 'Unable to get latest version info. Try again later.'
|
421
|
+
sys.exit(1)
|
422
|
+
|
423
|
+
# Do the version check
|
424
|
+
if updateInfo['version'] != agentConfig['version']:
|
425
|
+
import md5 # I know this is depreciated, but we still support Python 2.4 and hashlib is only in 2.5. Case 26918
|
426
|
+
import urllib
|
427
|
+
|
428
|
+
print 'A new version is available.'
|
429
|
+
|
430
|
+
def downloadFile(agentFile, recursed = False):
|
431
|
+
mainLogger.debug('Update: downloading ' + agentFile['name'])
|
432
|
+
print 'Downloading ' + agentFile['name']
|
433
|
+
|
434
|
+
downloadedFile = urllib.urlretrieve('http://www.serverdensity.com/downloads/sd-agent/' + agentFile['name'])
|
435
|
+
|
436
|
+
# Do md5 check to make sure the file downloaded properly
|
437
|
+
checksum = md5.new()
|
438
|
+
f = file(downloadedFile[0], 'rb')
|
439
|
+
|
440
|
+
# Although the files are small, we can't guarantee the available memory nor that there
|
441
|
+
# won't be large files in the future, so read the file in small parts (1kb at time)
|
442
|
+
while True:
|
443
|
+
part = f.read(1024)
|
444
|
+
|
445
|
+
if not part:
|
446
|
+
break # end of file
|
447
|
+
|
448
|
+
checksum.update(part)
|
449
|
+
|
450
|
+
f.close()
|
451
|
+
|
452
|
+
# Do we have a match?
|
453
|
+
if checksum.hexdigest() == agentFile['md5']:
|
454
|
+
return downloadedFile[0]
|
455
|
+
|
456
|
+
else:
|
457
|
+
# Try once more
|
458
|
+
if recursed == False:
|
459
|
+
downloadFile(agentFile, True)
|
460
|
+
|
461
|
+
else:
|
462
|
+
print agentFile['name'] + ' did not match its checksum - it is corrupted. This may be caused by network issues so please try again in a moment.'
|
463
|
+
sys.exit(1)
|
464
|
+
|
465
|
+
# Loop through the new files and call the download function
|
466
|
+
for agentFile in updateInfo['files']:
|
467
|
+
agentFile['tempFile'] = downloadFile(agentFile)
|
468
|
+
|
469
|
+
# If we got to here then everything worked out fine. However, all the files are still in temporary locations so we need to move them
|
470
|
+
# This is to stop an update breaking a working agent if the update fails halfway through
|
471
|
+
import os
|
472
|
+
import shutil # Prevents [Errno 18] Invalid cross-device link (case 26878) - http://mail.python.org/pipermail/python-list/2005-February/308026.html
|
473
|
+
|
474
|
+
for agentFile in updateInfo['files']:
|
475
|
+
mainLogger.debug('Update: updating ' + agentFile['name'])
|
476
|
+
print 'Updating ' + agentFile['name']
|
477
|
+
|
478
|
+
try:
|
479
|
+
if os.path.exists(agentFile['name']):
|
480
|
+
os.remove(agentFile['name'])
|
481
|
+
|
482
|
+
shutil.move(agentFile['tempFile'], agentFile['name'])
|
483
|
+
|
484
|
+
except OSError:
|
485
|
+
print 'An OS level error occurred. You will need to manually re-install the agent by downloading the latest version from http://www.serverdensity.com/downloads/sd-agent.tar.gz. You can copy your config.cfg to the new install'
|
486
|
+
sys.exit(1)
|
487
|
+
|
488
|
+
mainLogger.debug('Update: done')
|
489
|
+
|
490
|
+
print 'Update completed. Please restart the agent (python agent.py restart).'
|
491
|
+
|
492
|
+
else:
|
493
|
+
print 'The agent is already up to date'
|
494
|
+
|
495
|
+
else:
|
496
|
+
print 'Unknown command'
|
497
|
+
sys.exit(1)
|
498
|
+
|
499
|
+
sys.exit(0)
|
500
|
+
|
501
|
+
else:
|
502
|
+
print 'usage: %s start|stop|restart|status|update' % sys.argv[0]
|
503
|
+
sys.exit(1)
|