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/plugins.py
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
"""
|
4
|
+
Classes for plugin download, installation, and registration.
|
5
|
+
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
import ConfigParser
|
10
|
+
import os
|
11
|
+
import platform
|
12
|
+
import urllib, urllib2
|
13
|
+
from optparse import OptionParser
|
14
|
+
from zipfile import ZipFile
|
15
|
+
|
16
|
+
|
17
|
+
BASE_URL = 'http://plugins.serverdensity.com/'
|
18
|
+
|
19
|
+
python_version = platform.python_version_tuple()
|
20
|
+
|
21
|
+
if int(python_version[1]) >= 6:
|
22
|
+
import json
|
23
|
+
else:
|
24
|
+
import minjson
|
25
|
+
json = None
|
26
|
+
|
27
|
+
|
28
|
+
class App(object):
|
29
|
+
"""
|
30
|
+
Class for collecting arguments and options for the plugin
|
31
|
+
download and installation process.
|
32
|
+
|
33
|
+
"""
|
34
|
+
def __init__(self):
|
35
|
+
usage = 'usage: %prog [options] key'
|
36
|
+
self.parser = OptionParser(usage=usage)
|
37
|
+
self.parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
|
38
|
+
default=False, help='run in verbose mode')
|
39
|
+
self.parser.add_option('-r', '--remove', action='store_true', dest='remove',
|
40
|
+
default=False, help='remove plugin')
|
41
|
+
self.parser.add_option('-u', '--update', action='store_true', dest='update',
|
42
|
+
default=False, help='update installed plugins')
|
43
|
+
|
44
|
+
def run(self):
|
45
|
+
"""
|
46
|
+
Entry point to the plugin helper application.
|
47
|
+
|
48
|
+
"""
|
49
|
+
(options, args) = self.parser.parse_args()
|
50
|
+
if len(args) != 1:
|
51
|
+
if options.update:
|
52
|
+
updater = PluginUpdater(verbose=options.verbose)
|
53
|
+
updater.start()
|
54
|
+
return
|
55
|
+
else:
|
56
|
+
self.parser.error('incorrect number of arguments')
|
57
|
+
if options.remove:
|
58
|
+
remover = PluginRemover(key=args[0], verbose=options.verbose)
|
59
|
+
remover.start()
|
60
|
+
else:
|
61
|
+
downloader = PluginDownloader(key=args[0], verbose=options.verbose)
|
62
|
+
downloader.start()
|
63
|
+
|
64
|
+
class PluginMetadata(object):
|
65
|
+
def __init__(self, downloader=None):
|
66
|
+
self.downloader = downloader
|
67
|
+
|
68
|
+
def get(self):
|
69
|
+
raise Exception, 'sub-classes to provide implementation.'
|
70
|
+
|
71
|
+
def json(self):
|
72
|
+
metadata = self.get()
|
73
|
+
if self.downloader.verbose:
|
74
|
+
print metadata
|
75
|
+
if json:
|
76
|
+
return json.loads(metadata)
|
77
|
+
else:
|
78
|
+
return minjson.safeRead(metadata)
|
79
|
+
|
80
|
+
class FilePluginMetadata(PluginMetadata):
|
81
|
+
"""
|
82
|
+
File-based metadata provider, for testing purposes.
|
83
|
+
|
84
|
+
"""
|
85
|
+
def get(self):
|
86
|
+
path = os.path.join(os.path.dirname(__file__), 'tests/plugin.json')
|
87
|
+
if self.downloader.verbose:
|
88
|
+
print 'reading plugin data from %s' % path
|
89
|
+
f = open(path, 'r')
|
90
|
+
data = f.read()
|
91
|
+
f.close()
|
92
|
+
return data
|
93
|
+
|
94
|
+
class WebPluginMetadata(PluginMetadata):
|
95
|
+
"""
|
96
|
+
Web-based metadata provider.
|
97
|
+
|
98
|
+
"""
|
99
|
+
def __init__(self, downloader=None, agent_key=None):
|
100
|
+
super(WebPluginMetadata, self).__init__(downloader=downloader)
|
101
|
+
self.agent_key = agent_key
|
102
|
+
|
103
|
+
def get(self):
|
104
|
+
url = '%sinstall/' % BASE_URL
|
105
|
+
data = {
|
106
|
+
'installId': self.downloader.key,
|
107
|
+
'agentKey': self.agent_key
|
108
|
+
}
|
109
|
+
if self.downloader.verbose:
|
110
|
+
print 'sending %s to %s' % (data, url)
|
111
|
+
request = urllib2.urlopen(url, urllib.urlencode(data))
|
112
|
+
response = request.read()
|
113
|
+
return response
|
114
|
+
|
115
|
+
class Action(object):
|
116
|
+
def __init__(self, key=None, verbose=True):
|
117
|
+
self.key = key
|
118
|
+
self.verbose = verbose
|
119
|
+
|
120
|
+
def start(self):
|
121
|
+
raise Exception, 'sub-classes to provide implementation.'
|
122
|
+
|
123
|
+
class PluginUpdater(Action):
|
124
|
+
def __init__(self, verbose=True):
|
125
|
+
super(PluginUpdater, self).__init__(verbose=verbose)
|
126
|
+
|
127
|
+
def __get_installs(self):
|
128
|
+
url = '%supdate/' % BASE_URL
|
129
|
+
data = {
|
130
|
+
'agentKey': self.config.agent_key
|
131
|
+
}
|
132
|
+
request = urllib2.urlopen(url, urllib.urlencode(data))
|
133
|
+
response = request.read()
|
134
|
+
if json:
|
135
|
+
return json.loads(response)
|
136
|
+
else:
|
137
|
+
return minjson.safeRead(response)
|
138
|
+
|
139
|
+
def start(self):
|
140
|
+
self.config = AgentConfig(action=self)
|
141
|
+
if self.verbose:
|
142
|
+
print 'updating plugins'
|
143
|
+
installs = self.__get_installs()
|
144
|
+
for install_id in installs['installIds']:
|
145
|
+
PluginDownloader(key=install_id, verbose=self.verbose).start()
|
146
|
+
|
147
|
+
class PluginRemover(Action):
|
148
|
+
"""
|
149
|
+
Class for removing a plugin.
|
150
|
+
|
151
|
+
"""
|
152
|
+
def __init__(self, key=None, verbose=True):
|
153
|
+
super(PluginRemover, self).__init__(key=key, verbose=verbose)
|
154
|
+
|
155
|
+
def __send_removal(self):
|
156
|
+
url = '%suninstall/' % BASE_URL
|
157
|
+
data = {
|
158
|
+
'installId': self.key,
|
159
|
+
'agentKey': self.config.agent_key
|
160
|
+
}
|
161
|
+
request = urllib2.urlopen(url, urllib.urlencode(data))
|
162
|
+
response = request.read()
|
163
|
+
if json:
|
164
|
+
return json.loads(response)
|
165
|
+
else:
|
166
|
+
return minjson.safeRead(response)
|
167
|
+
|
168
|
+
def __remove_file(self, name):
|
169
|
+
name = '%s.py' % name
|
170
|
+
path = os.path.join(self.config.plugin_path, name)
|
171
|
+
if self.verbose:
|
172
|
+
print 'removing %s' % path
|
173
|
+
os.remove(path)
|
174
|
+
|
175
|
+
def start(self):
|
176
|
+
self.config = AgentConfig(action=self)
|
177
|
+
if self.verbose:
|
178
|
+
print 'removing plugin with install key:', self.key
|
179
|
+
response = self.__send_removal()
|
180
|
+
if self.verbose:
|
181
|
+
print 'retrieved remove response.'
|
182
|
+
assert 'status' in response, 'response is not valid.'
|
183
|
+
if 'status' in response and response['status'] == 'error':
|
184
|
+
raise Exception, response['msg']
|
185
|
+
self.__remove_file(response['name'])
|
186
|
+
print 'plugin removed successfully.'
|
187
|
+
|
188
|
+
class PluginDownloader(Action):
|
189
|
+
"""
|
190
|
+
Class for downloading a plugin.
|
191
|
+
|
192
|
+
"""
|
193
|
+
def __init__(self, key=None, verbose=True):
|
194
|
+
super(PluginDownloader, self).__init__(key=key, verbose=verbose)
|
195
|
+
|
196
|
+
def __prepare_plugin_directory(self):
|
197
|
+
if not os.path.exists(self.config.plugin_path):
|
198
|
+
if self.verbose:
|
199
|
+
print '%s does not exist, creating' % self.config.plugin_path
|
200
|
+
os.mkdir(self.config.plugin_path)
|
201
|
+
if self.verbose:
|
202
|
+
print '%s created' % self.config.plugin_path
|
203
|
+
elif self.verbose:
|
204
|
+
print '%s exists' % self.config.plugin_path
|
205
|
+
|
206
|
+
def __download(self):
|
207
|
+
self.url = '%sdownload/%s/%s/' % (BASE_URL, self.key, self.config.agent_key)
|
208
|
+
if self.verbose:
|
209
|
+
print 'downloading for agent %s: %s' % (self.config.agent_key, self.url)
|
210
|
+
request = urllib2.urlopen(self.url)
|
211
|
+
data = request.read()
|
212
|
+
path = os.path.join(self.config.plugin_path, '%s.zip' % self.key)
|
213
|
+
f = open(path, 'w')
|
214
|
+
f.write(data)
|
215
|
+
f.close()
|
216
|
+
z = ZipFile(path, 'r')
|
217
|
+
|
218
|
+
try:
|
219
|
+
if json:
|
220
|
+
if self.verbose:
|
221
|
+
print 'extract all: %s' % (os.path.dirname(path))
|
222
|
+
z.extractall(os.path.dirname(path))
|
223
|
+
else:
|
224
|
+
for name in z.namelist():
|
225
|
+
if self.verbose:
|
226
|
+
print 'extract loop: %s' % (os.path.join(os.path.dirname(path), name))
|
227
|
+
data = z.read(name)
|
228
|
+
f = open(os.path.join(os.path.dirname(path), name), 'w')
|
229
|
+
f.write(data)
|
230
|
+
f.close()
|
231
|
+
|
232
|
+
except Exception, ex:
|
233
|
+
print ex
|
234
|
+
|
235
|
+
z.close()
|
236
|
+
os.remove(path)
|
237
|
+
|
238
|
+
def start(self):
|
239
|
+
self.config = AgentConfig(action=self)
|
240
|
+
metadata = WebPluginMetadata(self, agent_key=self.config.agent_key).json()
|
241
|
+
if self.verbose:
|
242
|
+
print 'retrieved metadata.'
|
243
|
+
assert 'configKeys' in metadata or 'status' in metadata, 'metadata is not valid.'
|
244
|
+
if 'status' in metadata and metadata['status'] == 'error':
|
245
|
+
raise Exception, metadata['msg']
|
246
|
+
self.__prepare_plugin_directory()
|
247
|
+
self.__download()
|
248
|
+
self.config.prompt(metadata['configKeys'])
|
249
|
+
print 'plugin installed; please restart your agent'
|
250
|
+
|
251
|
+
class AgentConfig(object):
|
252
|
+
"""
|
253
|
+
Class for writing new config options to sd-agent config.
|
254
|
+
|
255
|
+
"""
|
256
|
+
def __init__(self, action=None):
|
257
|
+
self.action = action
|
258
|
+
self.path = self.__get_config_path()
|
259
|
+
assert self.path, 'no config path found.'
|
260
|
+
self.config = self.__parse()
|
261
|
+
if self.config.get('Main', 'plugin_directory'):
|
262
|
+
self.plugin_path = self.config.get('Main', 'plugin_directory')
|
263
|
+
self.agent_key = self.config.get('Main', 'agent_key')
|
264
|
+
assert self.agent_key, 'no agent key.'
|
265
|
+
|
266
|
+
def __get_config_path(self):
|
267
|
+
paths = (
|
268
|
+
'/etc/sd-agent/config.cfg',
|
269
|
+
os.path.join(os.path.dirname(__file__), 'config.cfg')
|
270
|
+
)
|
271
|
+
for path in paths:
|
272
|
+
if os.path.exists(path):
|
273
|
+
if self.action.verbose:
|
274
|
+
print 'found config at %s' % path
|
275
|
+
return path
|
276
|
+
|
277
|
+
def __parse(self):
|
278
|
+
if os.access(self.path, os.R_OK) == False:
|
279
|
+
if self.action.verbose:
|
280
|
+
print 'cannot access config'
|
281
|
+
raise Exception, 'cannot access config'
|
282
|
+
if self.action.verbose:
|
283
|
+
print 'found config, parsing'
|
284
|
+
config = ConfigParser.ConfigParser()
|
285
|
+
config.read(self.path)
|
286
|
+
if self.action.verbose:
|
287
|
+
print 'parsed config'
|
288
|
+
return config
|
289
|
+
|
290
|
+
def __write(self, values):
|
291
|
+
for key in values.keys():
|
292
|
+
self.config.set('Main', key, values[key])
|
293
|
+
try:
|
294
|
+
f = open(self.path, 'w')
|
295
|
+
self.config.write(f)
|
296
|
+
f.close()
|
297
|
+
except Exception, ex:
|
298
|
+
print ex
|
299
|
+
sys.exit(1)
|
300
|
+
|
301
|
+
def prompt(self, options):
|
302
|
+
if not options:
|
303
|
+
return
|
304
|
+
values = {}
|
305
|
+
for option in options:
|
306
|
+
if not self.config.has_option('Main', option):
|
307
|
+
values[option] = raw_input('value for %s: ' % option)
|
308
|
+
self.__write(values)
|
309
|
+
|
310
|
+
if __name__ == '__main__':
|
311
|
+
try:
|
312
|
+
app = App()
|
313
|
+
app.run()
|
314
|
+
except Exception, ex:
|
315
|
+
print 'error: %s' % ex
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
###############################################################################
|
4
|
+
# sd-agent
|
5
|
+
#
|
6
|
+
# Written by Boxed Ice <hello@boxedice.com>
|
7
|
+
# A server monitoring daemon for www.serverdensity.com
|
8
|
+
#
|
9
|
+
# Licensed under Simplified BSD License (see LICENSE)
|
10
|
+
#
|
11
|
+
###############################################################################
|
12
|
+
#
|
13
|
+
# chkconfig: 345 85 15
|
14
|
+
# description: Server Density Monitoring Agent
|
15
|
+
|
16
|
+
### BEGIN INIT INFO
|
17
|
+
# Provides: sd-agent
|
18
|
+
# Short-Description: Start and start sd-agent
|
19
|
+
# Description: sd-agent is the monitoring agent component for Server Density
|
20
|
+
# Required-Start:
|
21
|
+
# Required-Stop:
|
22
|
+
# Default-Start: 2 3 4 5
|
23
|
+
# Default-Stop: 0 1 6
|
24
|
+
### END INIT INFO
|
25
|
+
|
26
|
+
AGENTPATH="/usr/bin/sd-agent/agent.py"
|
27
|
+
AGENTUSER="sd-agent"
|
28
|
+
PIDPATH="/var/run/sd-agent/"
|
29
|
+
|
30
|
+
[ -f $AGENTPATH ] || echo "/usr/bin/sd-agent not found"
|
31
|
+
|
32
|
+
# Source function library.
|
33
|
+
if [ -f /etc/init.d/functions ]; then
|
34
|
+
. /etc/init.d/functions
|
35
|
+
fi
|
36
|
+
|
37
|
+
if [ -f /etc/SuSE-release ]; then
|
38
|
+
. /etc/rc.status
|
39
|
+
rc_reset
|
40
|
+
fi
|
41
|
+
|
42
|
+
# Action to take
|
43
|
+
case "$1" in
|
44
|
+
start)
|
45
|
+
if [ ! -d $PIDPATH ]; then
|
46
|
+
mkdir -p $PIDPATH
|
47
|
+
chown sd-agent:sd-agent $PIDPATH
|
48
|
+
fi
|
49
|
+
|
50
|
+
su $AGENTUSER -c "python $AGENTPATH stop init"
|
51
|
+
su $AGENTUSER -c "python $AGENTPATH start init --clean"
|
52
|
+
|
53
|
+
if [ -f /etc/SuSE-release ]; then
|
54
|
+
rc_status -v
|
55
|
+
elif [ -f /etc/debian_version ] || [ -f /etc/lsb-release ] || [ -f /etc/gentoo-release ]; then
|
56
|
+
echo " Started"
|
57
|
+
else
|
58
|
+
success
|
59
|
+
echo
|
60
|
+
fi
|
61
|
+
echo
|
62
|
+
;;
|
63
|
+
stop)
|
64
|
+
su $AGENTUSER -c "python $AGENTPATH stop init"
|
65
|
+
|
66
|
+
if [ -f /etc/SuSE-release ]; then
|
67
|
+
rc_status -v
|
68
|
+
elif [ -f /etc/debian_version ] || [ -f /etc/lsb-release ] || [ -f /etc/gentoo-release ]; then
|
69
|
+
echo " Stopped"
|
70
|
+
else
|
71
|
+
success
|
72
|
+
echo
|
73
|
+
fi
|
74
|
+
echo
|
75
|
+
;;
|
76
|
+
restart)
|
77
|
+
$0 stop
|
78
|
+
$0 start
|
79
|
+
;;
|
80
|
+
*)
|
81
|
+
echo "Usage: /etc/init.d/sd-agent start|stop|restart"
|
82
|
+
exit 1
|
83
|
+
esac
|
84
|
+
|
85
|
+
exit 0
|
data/bin/sd-agent.init
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
###############################################################################
|
4
|
+
# sd-agent
|
5
|
+
#
|
6
|
+
# Written by Boxed Ice <hello@boxedice.com>
|
7
|
+
# A server monitoring daemon for www.serverdensity.com
|
8
|
+
#
|
9
|
+
# Licensed under Simplified BSD License (see LICENSE)
|
10
|
+
#
|
11
|
+
###############################################################################
|
12
|
+
#
|
13
|
+
# chkconfig: 345 85 15
|
14
|
+
# description: Server Density Monitoring Agent
|
15
|
+
|
16
|
+
### BEGIN INIT INFO
|
17
|
+
# Provides: sd-agent
|
18
|
+
# Short-Description: Start and start sd-agent
|
19
|
+
# Description: sd-agent is the monitoring agent component for Server Density
|
20
|
+
### END INIT INFO
|
21
|
+
|
22
|
+
AGENTPATH="/usr/bin/sd-agent/agent.py"
|
23
|
+
|
24
|
+
[ -f $AGENTPATH ] || echo "/usr/bin/sd-agent not found"
|
25
|
+
|
26
|
+
# Source function library.
|
27
|
+
if [ -f /etc/init.d/functions ]; then
|
28
|
+
. /etc/init.d/functions
|
29
|
+
fi
|
30
|
+
|
31
|
+
if [ -f /etc/SuSE-release ]; then
|
32
|
+
. /etc/rc.status
|
33
|
+
rc_reset
|
34
|
+
fi
|
35
|
+
|
36
|
+
# Action to take
|
37
|
+
case "$1" in
|
38
|
+
start)
|
39
|
+
python $AGENTPATH stop init
|
40
|
+
python $AGENTPATH start init --clean
|
41
|
+
if [ -f /etc/SuSE-release ]; then
|
42
|
+
rc_status -v
|
43
|
+
elif [ -f /etc/debian_version ] || [ -f /etc/lsb-release ] || [ -f /etc/gentoo-release ]; then
|
44
|
+
echo " Started"
|
45
|
+
else
|
46
|
+
success
|
47
|
+
echo
|
48
|
+
fi
|
49
|
+
echo
|
50
|
+
;;
|
51
|
+
stop)
|
52
|
+
python $AGENTPATH stop init
|
53
|
+
|
54
|
+
if [ -f /etc/SuSE-release ]; then
|
55
|
+
rc_status -v
|
56
|
+
elif [ -f /etc/debian_version ] || [ -f /etc/lsb-release ] || [ -f /etc/gentoo-release ]; then
|
57
|
+
echo " Stopped"
|
58
|
+
else
|
59
|
+
success
|
60
|
+
echo
|
61
|
+
fi
|
62
|
+
echo
|
63
|
+
;;
|
64
|
+
restart)
|
65
|
+
$0 stop
|
66
|
+
$0 start
|
67
|
+
;;
|
68
|
+
*)
|
69
|
+
echo "Usage: /etc/init.d/sd-agent start|stop|restart"
|
70
|
+
exit 1
|
71
|
+
esac
|
72
|
+
|
73
|
+
exit 0
|