##// END OF EJS Templates
Added .pid files to ipcluster and ipcontroller and daemon mode....
Brian Granger -
Show More
@@ -15,6 +15,8 b' The IPython cluster directory'
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
19
18 import os
20 import os
19 import shutil
21 import shutil
20 import sys
22 import sys
@@ -37,6 +39,10 b' class ClusterDirError(Exception):'
37 pass
39 pass
38
40
39
41
42 class PIDFileError(Exception):
43 pass
44
45
40 class ClusterDir(Component):
46 class ClusterDir(Component):
41 """An object to manage the cluster directory and its resources.
47 """An object to manage the cluster directory and its resources.
42
48
@@ -50,9 +56,11 b' class ClusterDir(Component):'
50
56
51 security_dir_name = Unicode('security')
57 security_dir_name = Unicode('security')
52 log_dir_name = Unicode('log')
58 log_dir_name = Unicode('log')
53 security_dir = Unicode()
59 pid_dir_name = Unicode('pid')
54 log_dir = Unicode('')
60 security_dir = Unicode(u'')
55 location = Unicode('')
61 log_dir = Unicode(u'')
62 pid_dir = Unicode(u'')
63 location = Unicode(u'')
56
64
57 def __init__(self, location):
65 def __init__(self, location):
58 super(ClusterDir, self).__init__(None)
66 super(ClusterDir, self).__init__(None)
@@ -65,6 +73,7 b' class ClusterDir(Component):'
65 os.chmod(new, 0777)
73 os.chmod(new, 0777)
66 self.security_dir = os.path.join(new, self.security_dir_name)
74 self.security_dir = os.path.join(new, self.security_dir_name)
67 self.log_dir = os.path.join(new, self.log_dir_name)
75 self.log_dir = os.path.join(new, self.log_dir_name)
76 self.pid_dir = os.path.join(new, self.pid_dir_name)
68 self.check_dirs()
77 self.check_dirs()
69
78
70 def _log_dir_changed(self, name, old, new):
79 def _log_dir_changed(self, name, old, new):
@@ -85,9 +94,19 b' class ClusterDir(Component):'
85 else:
94 else:
86 os.chmod(self.security_dir, 0700)
95 os.chmod(self.security_dir, 0700)
87
96
97 def _pid_dir_changed(self, name, old, new):
98 self.check_pid_dir()
99
100 def check_pid_dir(self):
101 if not os.path.isdir(self.pid_dir):
102 os.mkdir(self.pid_dir, 0700)
103 else:
104 os.chmod(self.pid_dir, 0700)
105
88 def check_dirs(self):
106 def check_dirs(self):
89 self.check_security_dir()
107 self.check_security_dir()
90 self.check_log_dir()
108 self.check_log_dir()
109 self.check_pid_dir()
91
110
92 def load_config_file(self, filename):
111 def load_config_file(self, filename):
93 """Load a config file from the top level of the cluster dir.
112 """Load a config file from the top level of the cluster dir.
@@ -375,6 +394,8 b' class ApplicationWithClusterDir(Application):'
375 self.security_dir = config.Global.security_dir = sdir
394 self.security_dir = config.Global.security_dir = sdir
376 ldir = self.cluster_dir_obj.log_dir
395 ldir = self.cluster_dir_obj.log_dir
377 self.log_dir = config.Global.log_dir = ldir
396 self.log_dir = config.Global.log_dir = ldir
397 pdir = self.cluster_dir_obj.pid_dir
398 self.pid_dir = config.Global.pid_dir = pdir
378 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
399 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
379
400
380 def start_logging(self):
401 def start_logging(self):
@@ -392,3 +413,46 b' class ApplicationWithClusterDir(Application):'
392 else:
413 else:
393 open_log_file = sys.stdout
414 open_log_file = sys.stdout
394 log.startLogging(open_log_file)
415 log.startLogging(open_log_file)
416
417 def write_pid_file(self):
418 """Create a .pid file in the pid_dir with my pid.
419
420 This must be called after pre_construct, which sets `self.pid_dir`.
421 This raises :exc:`PIDFileError` if the pid file exists already.
422 """
423 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
424 if os.path.isfile(pid_file):
425 pid = self.get_pid_from_file()
426 raise PIDFileError(
427 'The pid file [%s] already exists. \nThis could mean that this '
428 'server is already running with [pid=%s].' % (pid_file, pid))
429 with open(pid_file, 'w') as f:
430 self.log.info("Creating pid file: %s" % pid_file)
431 f.write(repr(os.getpid())+'\n')
432
433 def remove_pid_file(self):
434 """Remove the pid file.
435
436 This should be called at shutdown by registering a callback with
437 :func:`reactor.addSystemEventTrigger`.
438 """
439 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
440 if os.path.isfile(pid_file):
441 try:
442 self.log.info("Removing pid file: %s" % pid_file)
443 os.remove(pid_file)
444 except:
445 pass
446
447 def get_pid_from_file(self):
448 """Get the pid from the pid file.
449
450 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
451 """
452 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
453 if os.path.isfile(pid_file):
454 with open(pid_file, 'r') as f:
455 pid = int(f.read().strip())
456 return pid
457 else:
458 raise PIDFileError('pid file not found: %s' % pid_file) No newline at end of file
@@ -20,13 +20,15 b' import os'
20 import signal
20 import signal
21 import sys
21 import sys
22
22
23 from twisted.scripts._twistd_unix import daemonize
24
23 from IPython.core import release
25 from IPython.core import release
24 from IPython.external import argparse
26 from IPython.external import argparse
25 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
27 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
26 from IPython.utils.importstring import import_item
28 from IPython.utils.importstring import import_item
27
29
28 from IPython.kernel.clusterdir import (
30 from IPython.kernel.clusterdir import (
29 ApplicationWithClusterDir, ClusterDirError
31 ApplicationWithClusterDir, ClusterDirError, PIDFileError
30 )
32 )
31
33
32 from twisted.internet import reactor, defer
34 from twisted.internet import reactor, defer
@@ -132,6 +134,27 b' class IPClusterCLLoader(ArgParseConfigLoader):'
132 help="Don't delete old log flies before starting.",
134 help="Don't delete old log flies before starting.",
133 default=NoConfigDefault
135 default=NoConfigDefault
134 )
136 )
137 parser_start.add_argument('--daemon', '-daemon',
138 dest='Global.daemonize', action='store_true',
139 help='Daemonize the ipcluster program. This implies --log-to-file',
140 default=NoConfigDefault
141 )
142 parser_start.add_argument('--nodaemon', '-nodaemon',
143 dest='Global.daemonize', action='store_false',
144 help="Dont't daemonize the ipcluster program.",
145 default=NoConfigDefault
146 )
147
148 parser_start = subparsers.add_parser(
149 'stop',
150 help='Stop a cluster.',
151 parents=[parent_parser1, parent_parser2]
152 )
153 parser_start.add_argument('-sig', '--sig',
154 dest='Global.stop_signal', type=int,
155 help="The signal number to use in stopping the cluster (default=2).",
156 default=NoConfigDefault
157 )
135
158
136 default_config_file_name = 'ipcluster_config.py'
159 default_config_file_name = 'ipcluster_config.py'
137
160
@@ -153,6 +176,8 b' class IPClusterApp(ApplicationWithClusterDir):'
153 self.default_config.Global.n = 2
176 self.default_config.Global.n = 2
154 self.default_config.Global.reset_config = False
177 self.default_config.Global.reset_config = False
155 self.default_config.Global.clean_logs = True
178 self.default_config.Global.clean_logs = True
179 self.default_config.Global.stop_signal = 2
180 self.default_config.Global.daemonize = False
156
181
157 def create_command_line_config(self):
182 def create_command_line_config(self):
158 """Create and return a command line config loader."""
183 """Create and return a command line config loader."""
@@ -170,7 +195,7 b' class IPClusterApp(ApplicationWithClusterDir):'
170 elif subcommand=='create':
195 elif subcommand=='create':
171 self.auto_create_cluster_dir = True
196 self.auto_create_cluster_dir = True
172 super(IPClusterApp, self).find_resources()
197 super(IPClusterApp, self).find_resources()
173 elif subcommand=='start':
198 elif subcommand=='start' or subcommand=='stop':
174 self.auto_create_cluster_dir = False
199 self.auto_create_cluster_dir = False
175 try:
200 try:
176 super(IPClusterApp, self).find_resources()
201 super(IPClusterApp, self).find_resources()
@@ -182,6 +207,16 b' class IPClusterApp(ApplicationWithClusterDir):'
182 "information about creating and listing cluster dirs."
207 "information about creating and listing cluster dirs."
183 )
208 )
184
209
210 def pre_construct(self):
211 super(IPClusterApp, self).pre_construct()
212 config = self.master_config
213 try:
214 daemon = config.Global.daemonize
215 if daemon:
216 config.Global.log_to_file = True
217 except AttributeError:
218 pass
219
185 def construct(self):
220 def construct(self):
186 config = self.master_config
221 config = self.master_config
187 if config.Global.subcommand=='list':
222 if config.Global.subcommand=='list':
@@ -288,11 +323,48 b' class IPClusterApp(ApplicationWithClusterDir):'
288 super(IPClusterApp, self).start_logging()
323 super(IPClusterApp, self).start_logging()
289
324
290 def start_app(self):
325 def start_app(self):
326 """Start the application, depending on what subcommand is used."""
291 config = self.master_config
327 config = self.master_config
292 if config.Global.subcommand=='create' or config.Global.subcommand=='list':
328 subcmd = config.Global.subcommand
329 if subcmd=='create' or subcmd=='list':
293 return
330 return
294 elif config.Global.subcommand=='start':
331 elif subcmd=='start':
332 # First see if the cluster is already running
333 try:
334 pid = self.get_pid_from_file()
335 except:
336 pass
337 else:
338 self.log.critical(
339 'Cluster is already running with [pid=%s]. '
340 'use "ipcluster stop" to stop the cluster.' % pid
341 )
342 sys.exit(9)
343 # Now log and daemonize
344 self.log.info('Starting ipcluster with [daemon=%r]' % config.Global.daemonize)
345 if config.Global.daemonize:
346 if os.name=='posix':
347 os.chdir(config.Global.cluster_dir)
348 self.log_level = 40
349 daemonize()
350
351 # Now write the new pid file after our new forked pid is active.
352 self.write_pid_file()
353 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
295 reactor.run()
354 reactor.run()
355 elif subcmd=='stop':
356 try:
357 pid = self.get_pid_from_file()
358 except PIDFileError:
359 self.log.critical(
360 'Problem reading pid file, cluster is probably not running.'
361 )
362 sys.exit(9)
363 sig = config.Global.stop_signal
364 self.log.info(
365 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
366 )
367 os.kill(pid, sig)
296
368
297
369
298 def launch_new_instance():
370 def launch_new_instance():
@@ -15,6 +15,8 b' The IPython controller application.'
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
19
18 import copy
20 import copy
19 import os
21 import os
20 import sys
22 import sys
@@ -213,7 +215,7 b' class IPControllerApp(ApplicationWithClusterDir):'
213
215
214 self.start_logging()
216 self.start_logging()
215 self.import_statements()
217 self.import_statements()
216
218
217 # Create the service hierarchy
219 # Create the service hierarchy
218 self.main_service = service.MultiService()
220 self.main_service = service.MultiService()
219 # The controller service
221 # The controller service
@@ -240,6 +242,8 b' class IPControllerApp(ApplicationWithClusterDir):'
240 def start_app(self):
242 def start_app(self):
241 # Start the controller service and set things running
243 # Start the controller service and set things running
242 self.main_service.startService()
244 self.main_service.startService()
245 self.write_pid_file()
246 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
243 reactor.run()
247 reactor.run()
244
248
245
249
General Comments 0
You need to be logged in to leave comments. Login now