1"""
2    Server Density
3    www.serverdensity.com
4    ----
5    Server monitoring agent for Linux, FreeBSD and Mac OS X
6
7    Licensed under Simplified BSD License (see LICENSE)
8"""
9import httplib
10import os
11import platform
12import shutil
13import socket
14import subprocess
15import sys
16import time
17import urllib
18import urllib2
19
20try:
21    from hashlib import md5
22except ImportError:  # Python < 2.5
23    from md5 import new as md5
24
25#
26# Why are you using this?
27#
28print 'Note: This script is for automating deployments and is not the normal way to install the SD agent. See http://www.serverdensity.com/docs/agent/installation/'
29print 'Continuing in 4 seconds...'
30time.sleep(4)
31
32#
33# Argument checks
34#
35
36if len(sys.argv) < 5:
37    print 'Usage: python sd-deploy.py [API URL] [SD URL] [username] [password] [[init]]'
38    sys.exit(2)
39
40#
41# Get server details
42#
43
44# IP
45try:
46    serverIp = socket.gethostbyname(socket.gethostname())
47
48except socket.error, e:
49    print 'Unable to get server IP: ' + str(e)
50    sys.exit(2)
51
52# Hostname
53try:
54    serverHostname = hostname = socket.getfqdn()
55
56except socket.error, e:
57    print 'Unable to get server hostname: ' + str(e)
58    sys.exit(2)
59
60#
61# Get latest agent version
62#
63
64print '1/4: Downloading latest agent version'
65
66# Request details
67try:
68    requestAgent = urllib2.urlopen('http://www.serverdensity.com/agentupdate/')
69    responseAgent = requestAgent.read()
70
71except urllib2.HTTPError, e:
72    print 'Unable to get latest version info - HTTPError = ' + str(e)
73    sys.exit(2)
74
75except urllib2.URLError, e:
76    print 'Unable to get latest version info - URLError = ' + str(e)
77    sys.exit(2)
78
79except httplib.HTTPException, e:
80    print 'Unable to get latest version info - HTTPException'
81    sys.exit(2)
82
83except Exception:
84    import traceback
85    print 'Unable to get latest version info - Exception = ' + traceback.format_exc()
86    sys.exit(2)
87
88
89#
90# Define downloader function
91#
92def downloadFile(agentFile, recursed=False):
93    print 'Downloading ' + agentFile['name']
94    downloadedFile = urllib.urlretrieve('http://www.serverdensity.com/downloads/sd-agent/' + agentFile['name'])
95
96    # Do md5 check to make sure the file downloaded properly
97    checksum = md5()
98    f = open(downloadedFile[0], 'rb')
99
100    # Although the files are small, we can't guarantee the available memory nor that there
101    # won't be large files in the future, so read the file in small parts (1kb at time)
102    while True:
103        part = f.read(1024)
104        if not part:
105            break  # end of file
106        checksum.update(part)
107
108    f.close()
109
110    # Do we have a match?
111    if checksum.hexdigest() == agentFile['md5']:
112        return downloadedFile[0]
113
114    else:
115        # Try once more
116        if not recursed:
117            downloadFile(agentFile, True)
118
119        else:
120            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.'
121            sys.exit(2)
122
123#
124# Install the agent files
125#
126
127# We need to return the data using JSON. As of Python 2.6+, there is a core JSON
128# module. We have a 2.4/2.5 compatible lib included with the agent but if we're
129# on 2.6 or above, we should use the core module which will be faster
130pythonVersion = platform.python_version_tuple()
131
132# Decode the JSON
133if int(pythonVersion[1]) >= 6:  # Don't bother checking major version since we only support v2 anyway
134    import json
135
136    try:
137        updateInfo = json.loads(responseAgent)
138    except Exception, e:
139        print 'Unable to get latest version info. Try again later.'
140        sys.exit(2)
141
142else:
143    import minjson
144
145    try:
146        updateInfo = minjson.safeRead(responseAgent)
147    except Exception, e:
148        print 'Unable to get latest version info. Try again later.'
149        sys.exit(2)
150
151# Loop through the new files and call the download function
152for agentFile in updateInfo['files']:
153    agentFile['tempFile'] = downloadFile(agentFile)
154
155# If we got to here then everything worked out fine. However, all the files are
156# still in temporary locations so we need to move them.
157#
158# Make sure doesn't exist already.
159# Usage of shutil prevents [Errno 18] Invalid cross-device link (case 26878) -
160# http://mail.python.org/pipermail/python-list/2005-February/308026.html
161if os.path.exists('sd-agent/'):
162    shutil.rmtree('sd-agent/')
163
164os.mkdir('sd-agent')
165
166for agentFile in updateInfo['files']:
167    print 'Installing ' + agentFile['name']
168
169    if agentFile['name'] != 'config.cfg':
170        shutil.move(agentFile['tempFile'], 'sd-agent/' + agentFile['name'])
171
172print 'Agent files downloaded'
173
174#
175# Call API to add new server
176#
177
178print '2/4: Adding new server'
179
180# Build API payload
181timestamp = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
182
183postData = urllib.urlencode({'name': serverHostname, 'ip': serverIp, 'notes': 'Added by sd-deploy: ' + timestamp})
184
185# Send request
186try:
187    # Password manager
188    mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
189    mgr.add_password(None, sys.argv[1] + '/1.0/', sys.argv[3], sys.argv[4])
190    opener = urllib2.build_opener(urllib2.HTTPBasicAuthHandler(mgr), urllib2.HTTPDigestAuthHandler(mgr))
191
192    urllib2.install_opener(opener)
193
194    # Build the request handler
195    requestAdd = urllib2.Request(sys.argv[1] + '/1.0/?account=' + sys.argv[2] + '&c=servers/add', postData, {'User-Agent': 'Server Density Deploy'})
196
197    # Do the request, log any errors
198    responseAdd = urllib2.urlopen(requestAdd)
199
200    readAdd = responseAdd.read()
201
202except urllib2.HTTPError, e:
203    print 'HTTPError = ' + str(e)
204
205    if os.path.exists('sd-agent/'):
206        shutil.rmtree('sd-agent/')
207
208except urllib2.URLError, e:
209    print 'URLError = ' + str(e)
210
211    if os.path.exists('sd-agent/'):
212        shutil.rmtree('sd-agent/')
213
214except httplib.HTTPException, e:  # Added for case #26701
215    print 'HTTPException' + str(e)
216
217    if os.path.exists('sd-agent/'):
218        shutil.rmtree('sd-agent/')
219
220except Exception, e:
221    import traceback
222    print 'Exception = ' + traceback.format_exc()
223
224    if os.path.exists('sd-agent/'):
225        shutil.rmtree('sd-agent/')
226
227# Decode the JSON
228if int(pythonVersion[1]) >= 6:  # Don't bother checking major version since we only support v2 anyway
229    import json
230
231    try:
232        serverInfo = json.loads(readAdd)
233    except Exception, e:
234        print 'Unable to add server.'
235
236        if os.path.exists('sd-agent/'):
237            shutil.rmtree('sd-agent/')
238
239        sys.exit(2)
240
241else:
242    import minjson
243
244    try:
245        serverInfo = minjson.safeRead(readAdd)
246    except Exception, e:
247        print 'Unable to add server.'
248
249        if os.path.exists('sd-agent/'):
250            shutil.rmtree('sd-agent/')
251
252        sys.exit(2)
253
254print 'Server added - ID: ' + str(serverInfo['data']['serverId'])
255
256#
257# Write config file
258#
259
260print '3/4: Writing config file'
261
262configCfg = '[Main]\nsd_url: http://' + sys.argv[2] + '\nagent_key: ' + serverInfo['data']['agentKey'] + '\napache_status_url: http://www.example.com/server-status/?auto'
263
264try:
265    f = open('sd-agent/config.cfg', 'w')
266    f.write(configCfg)
267    f.close()
268
269except Exception:
270    import traceback
271    print 'Exception = ' + traceback.format_exc()
272
273    if os.path.exists('sd-agent/'):
274        shutil.rmtree('sd-agent/')
275
276print 'Config file written'
277
278#
279# Install init.d
280#
281
282if len(sys.argv) == 6:
283
284    print '4/4: Installing init.d script'
285
286    shutil.copy('sd-agent/sd-agent.init', '/etc/init.d/sd-agent')
287
288    print 'Setting permissions'
289
290    df = subprocess.Popen(['chmod', '0755', '/etc/init.d/sd-agent'], stdout=subprocess.PIPE).communicate()[0]
291
292    print 'chkconfig'
293
294    df = subprocess.Popen(['chkconfig', '--add', 'sd-agent'], stdout=subprocess.PIPE).communicate()[0]
295
296    print 'Setting paths'
297
298    path = os.path.realpath(__file__)
299    path = os.path.dirname(path)
300
301    df = subprocess.Popen(['ln', '-s', path + '/sd-agent/', '/usr/bin/sd-agent'], stdout=subprocess.PIPE).communicate()[0]
302
303    print 'Install completed'
304
305    print 'Launch: /etc/init.d/sd-agent start'
306
307else:
308
309    print '4/4: Not installing init.d script'
310    print 'Install completed'
311
312    path = os.path.realpath(__file__)
313    path = os.path.dirname(path)
314
315    print 'Launch: python ' + path + '/sd-agent/agent.py start'
316