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/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