##// END OF EJS Templates
Minors fixes on Windows....
bgranger -
Show More
@@ -1,458 +1,461 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython cluster directory
4 The IPython cluster directory
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import os
20 import os
21 import shutil
21 import shutil
22 import sys
22 import sys
23
23
24 from twisted.python import log
24 from twisted.python import log
25
25
26 from IPython.core import release
26 from IPython.core import release
27 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application
28 from IPython.core.application import Application
29 from IPython.core.component import Component
29 from IPython.core.component import Component
30 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
30 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
31 from IPython.utils.traitlets import Unicode, Bool
31 from IPython.utils.traitlets import Unicode, Bool
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Imports
34 # Imports
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37
37
38 class ClusterDirError(Exception):
38 class ClusterDirError(Exception):
39 pass
39 pass
40
40
41
41
42 class PIDFileError(Exception):
42 class PIDFileError(Exception):
43 pass
43 pass
44
44
45
45
46 class ClusterDir(Component):
46 class ClusterDir(Component):
47 """An object to manage the cluster directory and its resources.
47 """An object to manage the cluster directory and its resources.
48
48
49 The cluster directory is used by :command:`ipcontroller`,
49 The cluster directory is used by :command:`ipcontroller`,
50 :command:`ipcontroller` and :command:`ipcontroller` to manage the
50 :command:`ipcontroller` and :command:`ipcontroller` to manage the
51 configuration, logging and security of these applications.
51 configuration, logging and security of these applications.
52
52
53 This object knows how to find, create and manage these directories. This
53 This object knows how to find, create and manage these directories. This
54 should be used by any code that want's to handle cluster directories.
54 should be used by any code that want's to handle cluster directories.
55 """
55 """
56
56
57 security_dir_name = Unicode('security')
57 security_dir_name = Unicode('security')
58 log_dir_name = Unicode('log')
58 log_dir_name = Unicode('log')
59 pid_dir_name = Unicode('pid')
59 pid_dir_name = Unicode('pid')
60 security_dir = Unicode(u'')
60 security_dir = Unicode(u'')
61 log_dir = Unicode(u'')
61 log_dir = Unicode(u'')
62 pid_dir = Unicode(u'')
62 pid_dir = Unicode(u'')
63 location = Unicode(u'')
63 location = Unicode(u'')
64
64
65 def __init__(self, location):
65 def __init__(self, location):
66 super(ClusterDir, self).__init__(None)
66 super(ClusterDir, self).__init__(None)
67 self.location = location
67 self.location = location
68
68
69 def _location_changed(self, name, old, new):
69 def _location_changed(self, name, old, new):
70 if not os.path.isdir(new):
70 if not os.path.isdir(new):
71 os.makedirs(new, mode=0777)
71 os.makedirs(new, mode=0777)
72 else:
72 else:
73 os.chmod(new, 0777)
73 os.chmod(new, 0777)
74 self.security_dir = os.path.join(new, self.security_dir_name)
74 self.security_dir = os.path.join(new, self.security_dir_name)
75 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)
76 self.pid_dir = os.path.join(new, self.pid_dir_name)
77 self.check_dirs()
77 self.check_dirs()
78
78
79 def _log_dir_changed(self, name, old, new):
79 def _log_dir_changed(self, name, old, new):
80 self.check_log_dir()
80 self.check_log_dir()
81
81
82 def check_log_dir(self):
82 def check_log_dir(self):
83 if not os.path.isdir(self.log_dir):
83 if not os.path.isdir(self.log_dir):
84 os.mkdir(self.log_dir, 0777)
84 os.mkdir(self.log_dir, 0777)
85 else:
85 else:
86 os.chmod(self.log_dir, 0777)
86 os.chmod(self.log_dir, 0777)
87
87
88 def _security_dir_changed(self, name, old, new):
88 def _security_dir_changed(self, name, old, new):
89 self.check_security_dir()
89 self.check_security_dir()
90
90
91 def check_security_dir(self):
91 def check_security_dir(self):
92 if not os.path.isdir(self.security_dir):
92 if not os.path.isdir(self.security_dir):
93 os.mkdir(self.security_dir, 0700)
93 os.mkdir(self.security_dir, 0700)
94 else:
94 else:
95 os.chmod(self.security_dir, 0700)
95 os.chmod(self.security_dir, 0700)
96
96
97 def _pid_dir_changed(self, name, old, new):
97 def _pid_dir_changed(self, name, old, new):
98 self.check_pid_dir()
98 self.check_pid_dir()
99
99
100 def check_pid_dir(self):
100 def check_pid_dir(self):
101 if not os.path.isdir(self.pid_dir):
101 if not os.path.isdir(self.pid_dir):
102 os.mkdir(self.pid_dir, 0700)
102 os.mkdir(self.pid_dir, 0700)
103 else:
103 else:
104 os.chmod(self.pid_dir, 0700)
104 os.chmod(self.pid_dir, 0700)
105
105
106 def check_dirs(self):
106 def check_dirs(self):
107 self.check_security_dir()
107 self.check_security_dir()
108 self.check_log_dir()
108 self.check_log_dir()
109 self.check_pid_dir()
109 self.check_pid_dir()
110
110
111 def load_config_file(self, filename):
111 def load_config_file(self, filename):
112 """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.
113
113
114 Parameters
114 Parameters
115 ----------
115 ----------
116 filename : unicode or str
116 filename : unicode or str
117 The filename only of the config file that must be located in
117 The filename only of the config file that must be located in
118 the top-level of the cluster directory.
118 the top-level of the cluster directory.
119 """
119 """
120 loader = PyFileConfigLoader(filename, self.location)
120 loader = PyFileConfigLoader(filename, self.location)
121 return loader.load_config()
121 return loader.load_config()
122
122
123 def copy_config_file(self, config_file, path=None, overwrite=False):
123 def copy_config_file(self, config_file, path=None, overwrite=False):
124 """Copy a default config file into the active cluster directory.
124 """Copy a default config file into the active cluster directory.
125
125
126 Default configuration files are kept in :mod:`IPython.config.default`.
126 Default configuration files are kept in :mod:`IPython.config.default`.
127 This function moves these from that location to the working cluster
127 This function moves these from that location to the working cluster
128 directory.
128 directory.
129 """
129 """
130 if path is None:
130 if path is None:
131 import IPython.config.default
131 import IPython.config.default
132 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
132 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
133 path = os.path.sep.join(path)
133 path = os.path.sep.join(path)
134 src = os.path.join(path, config_file)
134 src = os.path.join(path, config_file)
135 dst = os.path.join(self.location, config_file)
135 dst = os.path.join(self.location, config_file)
136 if not os.path.isfile(dst) or overwrite:
136 if not os.path.isfile(dst) or overwrite:
137 shutil.copy(src, dst)
137 shutil.copy(src, dst)
138
138
139 def copy_all_config_files(self, path=None, overwrite=False):
139 def copy_all_config_files(self, path=None, overwrite=False):
140 """Copy all config files into the active cluster directory."""
140 """Copy all config files into the active cluster directory."""
141 for f in ['ipcontroller_config.py', 'ipengine_config.py',
141 for f in ['ipcontroller_config.py', 'ipengine_config.py',
142 'ipcluster_config.py']:
142 'ipcluster_config.py']:
143 self.copy_config_file(f, path=path, overwrite=overwrite)
143 self.copy_config_file(f, path=path, overwrite=overwrite)
144
144
145 @classmethod
145 @classmethod
146 def create_cluster_dir(csl, cluster_dir):
146 def create_cluster_dir(csl, cluster_dir):
147 """Create a new cluster directory given a full path.
147 """Create a new cluster directory given a full path.
148
148
149 Parameters
149 Parameters
150 ----------
150 ----------
151 cluster_dir : str
151 cluster_dir : str
152 The full path to the cluster directory. If it does exist, it will
152 The full path to the cluster directory. If it does exist, it will
153 be used. If not, it will be created.
153 be used. If not, it will be created.
154 """
154 """
155 return ClusterDir(cluster_dir)
155 return ClusterDir(cluster_dir)
156
156
157 @classmethod
157 @classmethod
158 def create_cluster_dir_by_profile(cls, path, profile='default'):
158 def create_cluster_dir_by_profile(cls, path, profile='default'):
159 """Create a cluster dir by profile name and path.
159 """Create a cluster dir by profile name and path.
160
160
161 Parameters
161 Parameters
162 ----------
162 ----------
163 path : str
163 path : str
164 The path (directory) to put the cluster directory in.
164 The path (directory) to put the cluster directory in.
165 profile : str
165 profile : str
166 The name of the profile. The name of the cluster directory will
166 The name of the profile. The name of the cluster directory will
167 be "cluster_<profile>".
167 be "cluster_<profile>".
168 """
168 """
169 if not os.path.isdir(path):
169 if not os.path.isdir(path):
170 raise ClusterDirError('Directory not found: %s' % path)
170 raise ClusterDirError('Directory not found: %s' % path)
171 cluster_dir = os.path.join(path, 'cluster_' + profile)
171 cluster_dir = os.path.join(path, 'cluster_' + profile)
172 return ClusterDir(cluster_dir)
172 return ClusterDir(cluster_dir)
173
173
174 @classmethod
174 @classmethod
175 def find_cluster_dir_by_profile(cls, ipythondir, profile='default'):
175 def find_cluster_dir_by_profile(cls, ipythondir, profile='default'):
176 """Find an existing cluster dir by profile name, return its ClusterDir.
176 """Find an existing cluster dir by profile name, return its ClusterDir.
177
177
178 This searches through a sequence of paths for a cluster dir. If it
178 This searches through a sequence of paths for a cluster dir. If it
179 is not found, a :class:`ClusterDirError` exception will be raised.
179 is not found, a :class:`ClusterDirError` exception will be raised.
180
180
181 The search path algorithm is:
181 The search path algorithm is:
182 1. ``os.getcwd()``
182 1. ``os.getcwd()``
183 2. ``ipythondir``
183 2. ``ipythondir``
184 3. The directories found in the ":" separated
184 3. The directories found in the ":" separated
185 :env:`IPCLUSTERDIR_PATH` environment variable.
185 :env:`IPCLUSTERDIR_PATH` environment variable.
186
186
187 Parameters
187 Parameters
188 ----------
188 ----------
189 ipythondir : unicode or str
189 ipythondir : unicode or str
190 The IPython directory to use.
190 The IPython directory to use.
191 profile : unicode or str
191 profile : unicode or str
192 The name of the profile. The name of the cluster directory
192 The name of the profile. The name of the cluster directory
193 will be "cluster_<profile>".
193 will be "cluster_<profile>".
194 """
194 """
195 dirname = 'cluster_' + profile
195 dirname = 'cluster_' + profile
196 cluster_dir_paths = os.environ.get('IPCLUSTERDIR_PATH','')
196 cluster_dir_paths = os.environ.get('IPCLUSTERDIR_PATH','')
197 if cluster_dir_paths:
197 if cluster_dir_paths:
198 cluster_dir_paths = cluster_dir_paths.split(':')
198 cluster_dir_paths = cluster_dir_paths.split(':')
199 else:
199 else:
200 cluster_dir_paths = []
200 cluster_dir_paths = []
201 paths = [os.getcwd(), ipythondir] + cluster_dir_paths
201 paths = [os.getcwd(), ipythondir] + cluster_dir_paths
202 for p in paths:
202 for p in paths:
203 cluster_dir = os.path.join(p, dirname)
203 cluster_dir = os.path.join(p, dirname)
204 if os.path.isdir(cluster_dir):
204 if os.path.isdir(cluster_dir):
205 return ClusterDir(cluster_dir)
205 return ClusterDir(cluster_dir)
206 else:
206 else:
207 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
207 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
208
208
209 @classmethod
209 @classmethod
210 def find_cluster_dir(cls, cluster_dir):
210 def find_cluster_dir(cls, cluster_dir):
211 """Find/create a cluster dir and return its ClusterDir.
211 """Find/create a cluster dir and return its ClusterDir.
212
212
213 This will create the cluster directory if it doesn't exist.
213 This will create the cluster directory if it doesn't exist.
214
214
215 Parameters
215 Parameters
216 ----------
216 ----------
217 cluster_dir : unicode or str
217 cluster_dir : unicode or str
218 The path of the cluster directory. This is expanded using
218 The path of the cluster directory. This is expanded using
219 :func:`os.path.expandvars` and :func:`os.path.expanduser`.
219 :func:`os.path.expandvars` and :func:`os.path.expanduser`.
220 """
220 """
221 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
221 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
222 if not os.path.isdir(cluster_dir):
222 if not os.path.isdir(cluster_dir):
223 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
223 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
224 return ClusterDir(cluster_dir)
224 return ClusterDir(cluster_dir)
225
225
226
226
227 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
227 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
228 """Default command line options for IPython cluster applications."""
228 """Default command line options for IPython cluster applications."""
229
229
230 def _add_other_arguments(self):
230 def _add_other_arguments(self):
231 self.parser.add_argument('--ipython-dir',
231 self.parser.add_argument('--ipython-dir',
232 dest='Global.ipythondir',type=str,
232 dest='Global.ipythondir',type=str,
233 help='Set to override default location of Global.ipythondir.',
233 help='Set to override default location of Global.ipythondir.',
234 default=NoConfigDefault,
234 default=NoConfigDefault,
235 metavar='Global.ipythondir'
235 metavar='Global.ipythondir'
236 )
236 )
237 self.parser.add_argument('-p', '--profile',
237 self.parser.add_argument('-p', '--profile',
238 dest='Global.profile',type=str,
238 dest='Global.profile',type=str,
239 help='The string name of the profile to be used. This determines '
239 help='The string name of the profile to be used. This determines '
240 'the name of the cluster dir as: cluster_<profile>. The default profile '
240 'the name of the cluster dir as: cluster_<profile>. The default profile '
241 'is named "default". The cluster directory is resolve this way '
241 'is named "default". The cluster directory is resolve this way '
242 'if the --cluster-dir option is not used.',
242 'if the --cluster-dir option is not used.',
243 default=NoConfigDefault,
243 default=NoConfigDefault,
244 metavar='Global.profile'
244 metavar='Global.profile'
245 )
245 )
246 self.parser.add_argument('--log-level',
246 self.parser.add_argument('--log-level',
247 dest="Global.log_level",type=int,
247 dest="Global.log_level",type=int,
248 help='Set the log level (0,10,20,30,40,50). Default is 30.',
248 help='Set the log level (0,10,20,30,40,50). Default is 30.',
249 default=NoConfigDefault,
249 default=NoConfigDefault,
250 metavar="Global.log_level"
250 metavar="Global.log_level"
251 )
251 )
252 self.parser.add_argument('--cluster-dir',
252 self.parser.add_argument('--cluster-dir',
253 dest='Global.cluster_dir',type=str,
253 dest='Global.cluster_dir',type=str,
254 help='Set the cluster dir. This overrides the logic used by the '
254 help='Set the cluster dir. This overrides the logic used by the '
255 '--profile option.',
255 '--profile option.',
256 default=NoConfigDefault,
256 default=NoConfigDefault,
257 metavar='Global.cluster_dir'
257 metavar='Global.cluster_dir'
258 )
258 )
259 self.parser.add_argument('--clean-logs',
259 self.parser.add_argument('--clean-logs',
260 dest='Global.clean_logs', action='store_true',
260 dest='Global.clean_logs', action='store_true',
261 help='Delete old log flies before starting.',
261 help='Delete old log flies before starting.',
262 default=NoConfigDefault
262 default=NoConfigDefault
263 )
263 )
264 self.parser.add_argument('--no-clean-logs',
264 self.parser.add_argument('--no-clean-logs',
265 dest='Global.clean_logs', action='store_false',
265 dest='Global.clean_logs', action='store_false',
266 help="Don't Delete old log flies before starting.",
266 help="Don't Delete old log flies before starting.",
267 default=NoConfigDefault
267 default=NoConfigDefault
268 )
268 )
269
269
270 class ApplicationWithClusterDir(Application):
270 class ApplicationWithClusterDir(Application):
271 """An application that puts everything into a cluster directory.
271 """An application that puts everything into a cluster directory.
272
272
273 Instead of looking for things in the ipythondir, this type of application
273 Instead of looking for things in the ipythondir, this type of application
274 will use its own private directory called the "cluster directory"
274 will use its own private directory called the "cluster directory"
275 for things like config files, log files, etc.
275 for things like config files, log files, etc.
276
276
277 The cluster directory is resolved as follows:
277 The cluster directory is resolved as follows:
278
278
279 * If the ``--cluster-dir`` option is given, it is used.
279 * If the ``--cluster-dir`` option is given, it is used.
280 * If ``--cluster-dir`` is not given, the application directory is
280 * If ``--cluster-dir`` is not given, the application directory is
281 resolve using the profile name as ``cluster_<profile>``. The search
281 resolve using the profile name as ``cluster_<profile>``. The search
282 path for this directory is then i) cwd if it is found there
282 path for this directory is then i) cwd if it is found there
283 and ii) in ipythondir otherwise.
283 and ii) in ipythondir otherwise.
284
284
285 The config file for the application is to be put in the cluster
285 The config file for the application is to be put in the cluster
286 dir and named the value of the ``config_file_name`` class attribute.
286 dir and named the value of the ``config_file_name`` class attribute.
287 """
287 """
288
288
289 auto_create_cluster_dir = True
289 auto_create_cluster_dir = True
290
290
291 def create_default_config(self):
291 def create_default_config(self):
292 super(ApplicationWithClusterDir, self).create_default_config()
292 super(ApplicationWithClusterDir, self).create_default_config()
293 self.default_config.Global.profile = 'default'
293 self.default_config.Global.profile = 'default'
294 self.default_config.Global.cluster_dir = ''
294 self.default_config.Global.cluster_dir = ''
295 self.default_config.Global.log_to_file = False
295 self.default_config.Global.log_to_file = False
296 self.default_config.Global.clean_logs = False
296 self.default_config.Global.clean_logs = False
297
297
298 def create_command_line_config(self):
298 def create_command_line_config(self):
299 """Create and return a command line config loader."""
299 """Create and return a command line config loader."""
300 return AppWithClusterDirArgParseConfigLoader(
300 return AppWithClusterDirArgParseConfigLoader(
301 description=self.description,
301 description=self.description,
302 version=release.version
302 version=release.version
303 )
303 )
304
304
305 def find_resources(self):
305 def find_resources(self):
306 """This resolves the cluster directory.
306 """This resolves the cluster directory.
307
307
308 This tries to find the cluster directory and if successful, it will
308 This tries to find the cluster directory and if successful, it will
309 have done:
309 have done:
310 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
310 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
311 the application.
311 the application.
312 * Sets ``self.cluster_dir`` attribute of the application and config
312 * Sets ``self.cluster_dir`` attribute of the application and config
313 objects.
313 objects.
314
314
315 The algorithm used for this is as follows:
315 The algorithm used for this is as follows:
316 1. Try ``Global.cluster_dir``.
316 1. Try ``Global.cluster_dir``.
317 2. Try using ``Global.profile``.
317 2. Try using ``Global.profile``.
318 3. If both of these fail and ``self.auto_create_cluster_dir`` is
318 3. If both of these fail and ``self.auto_create_cluster_dir`` is
319 ``True``, then create the new cluster dir in the IPython directory.
319 ``True``, then create the new cluster dir in the IPython directory.
320 4. If all fails, then raise :class:`ClusterDirError`.
320 4. If all fails, then raise :class:`ClusterDirError`.
321 """
321 """
322
322
323 try:
323 try:
324 cluster_dir = self.command_line_config.Global.cluster_dir
324 cluster_dir = self.command_line_config.Global.cluster_dir
325 except AttributeError:
325 except AttributeError:
326 cluster_dir = self.default_config.Global.cluster_dir
326 cluster_dir = self.default_config.Global.cluster_dir
327 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
327 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
328 try:
328 try:
329 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
329 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
330 except ClusterDirError:
330 except ClusterDirError:
331 pass
331 pass
332 else:
332 else:
333 self.log.info('Using existing cluster dir: %s' % \
333 self.log.info('Using existing cluster dir: %s' % \
334 self.cluster_dir_obj.location
334 self.cluster_dir_obj.location
335 )
335 )
336 self.finish_cluster_dir()
336 self.finish_cluster_dir()
337 return
337 return
338
338
339 try:
339 try:
340 self.profile = self.command_line_config.Global.profile
340 self.profile = self.command_line_config.Global.profile
341 except AttributeError:
341 except AttributeError:
342 self.profile = self.default_config.Global.profile
342 self.profile = self.default_config.Global.profile
343 try:
343 try:
344 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
344 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
345 self.ipythondir, self.profile)
345 self.ipythondir, self.profile)
346 except ClusterDirError:
346 except ClusterDirError:
347 pass
347 pass
348 else:
348 else:
349 self.log.info('Using existing cluster dir: %s' % \
349 self.log.info('Using existing cluster dir: %s' % \
350 self.cluster_dir_obj.location
350 self.cluster_dir_obj.location
351 )
351 )
352 self.finish_cluster_dir()
352 self.finish_cluster_dir()
353 return
353 return
354
354
355 if self.auto_create_cluster_dir:
355 if self.auto_create_cluster_dir:
356 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
356 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
357 self.ipythondir, self.profile
357 self.ipythondir, self.profile
358 )
358 )
359 self.log.info('Creating new cluster dir: %s' % \
359 self.log.info('Creating new cluster dir: %s' % \
360 self.cluster_dir_obj.location
360 self.cluster_dir_obj.location
361 )
361 )
362 self.finish_cluster_dir()
362 self.finish_cluster_dir()
363 else:
363 else:
364 raise ClusterDirError('Could not find a valid cluster directory.')
364 raise ClusterDirError('Could not find a valid cluster directory.')
365
365
366 def finish_cluster_dir(self):
366 def finish_cluster_dir(self):
367 # Set the cluster directory
367 # Set the cluster directory
368 self.cluster_dir = self.cluster_dir_obj.location
368 self.cluster_dir = self.cluster_dir_obj.location
369
369
370 # These have to be set because they could be different from the one
370 # These have to be set because they could be different from the one
371 # that we just computed. Because command line has the highest
371 # that we just computed. Because command line has the highest
372 # priority, this will always end up in the master_config.
372 # priority, this will always end up in the master_config.
373 self.default_config.Global.cluster_dir = self.cluster_dir
373 self.default_config.Global.cluster_dir = self.cluster_dir
374 self.command_line_config.Global.cluster_dir = self.cluster_dir
374 self.command_line_config.Global.cluster_dir = self.cluster_dir
375
375
376 # Set the search path to the cluster directory
376 # Set the search path to the cluster directory
377 self.config_file_paths = (self.cluster_dir,)
377 self.config_file_paths = (self.cluster_dir,)
378
378
379 def find_config_file_name(self):
379 def find_config_file_name(self):
380 """Find the config file name for this application."""
380 """Find the config file name for this application."""
381 # For this type of Application it should be set as a class attribute.
381 # For this type of Application it should be set as a class attribute.
382 if not hasattr(self, 'config_file_name'):
382 if not hasattr(self, 'config_file_name'):
383 self.log.critical("No config filename found")
383 self.log.critical("No config filename found")
384
384
385 def find_config_file_paths(self):
385 def find_config_file_paths(self):
386 # Set the search path to the cluster directory
386 # Set the search path to the cluster directory
387 self.config_file_paths = (self.cluster_dir,)
387 self.config_file_paths = (self.cluster_dir,)
388
388
389 def pre_construct(self):
389 def pre_construct(self):
390 # The log and security dirs were set earlier, but here we put them
390 # The log and security dirs were set earlier, but here we put them
391 # into the config and log them.
391 # into the config and log them.
392 config = self.master_config
392 config = self.master_config
393 sdir = self.cluster_dir_obj.security_dir
393 sdir = self.cluster_dir_obj.security_dir
394 self.security_dir = config.Global.security_dir = sdir
394 self.security_dir = config.Global.security_dir = sdir
395 ldir = self.cluster_dir_obj.log_dir
395 ldir = self.cluster_dir_obj.log_dir
396 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
397 pdir = self.cluster_dir_obj.pid_dir
398 self.pid_dir = config.Global.pid_dir = pdir
398 self.pid_dir = config.Global.pid_dir = pdir
399 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
399 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
400
400
401 def start_logging(self):
401 def start_logging(self):
402 # Remove old log files
402 # Remove old log files
403 if self.master_config.Global.clean_logs:
403 if self.master_config.Global.clean_logs:
404 log_dir = self.master_config.Global.log_dir
404 log_dir = self.master_config.Global.log_dir
405 for f in os.listdir(log_dir):
405 for f in os.listdir(log_dir):
406 if f.startswith(self.name + '-') and f.endswith('.log'):
406 if f.startswith(self.name + '-') and f.endswith('.log'):
407 os.remove(os.path.join(log_dir, f))
407 os.remove(os.path.join(log_dir, f))
408 # Start logging to the new log file
408 # Start logging to the new log file
409 if self.master_config.Global.log_to_file:
409 if self.master_config.Global.log_to_file:
410 log_filename = self.name + '-' + str(os.getpid()) + '.log'
410 log_filename = self.name + '-' + str(os.getpid()) + '.log'
411 logfile = os.path.join(self.log_dir, log_filename)
411 logfile = os.path.join(self.log_dir, log_filename)
412 open_log_file = open(logfile, 'w')
412 open_log_file = open(logfile, 'w')
413 else:
413 else:
414 open_log_file = sys.stdout
414 open_log_file = sys.stdout
415 log.startLogging(open_log_file)
415 log.startLogging(open_log_file)
416
416
417 def write_pid_file(self):
417 def write_pid_file(self, overwrite=False):
418 """Create a .pid file in the pid_dir with my pid.
418 """Create a .pid file in the pid_dir with my pid.
419
419
420 This must be called after pre_construct, which sets `self.pid_dir`.
420 This must be called after pre_construct, which sets `self.pid_dir`.
421 This raises :exc:`PIDFileError` if the pid file exists already.
421 This raises :exc:`PIDFileError` if the pid file exists already.
422 """
422 """
423 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
423 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
424 if os.path.isfile(pid_file):
424 if os.path.isfile(pid_file):
425 pid = self.get_pid_from_file()
425 pid = self.get_pid_from_file()
426 raise PIDFileError(
426 if not overwrite:
427 'The pid file [%s] already exists. \nThis could mean that this '
427 raise PIDFileError(
428 'server is already running with [pid=%s].' % (pid_file, pid))
428 'The pid file [%s] already exists. \nThis could mean that this '
429 'server is already running with [pid=%s].' % (pid_file, pid)
430 )
429 with open(pid_file, 'w') as f:
431 with open(pid_file, 'w') as f:
430 self.log.info("Creating pid file: %s" % pid_file)
432 self.log.info("Creating pid file: %s" % pid_file)
431 f.write(repr(os.getpid())+'\n')
433 f.write(repr(os.getpid())+'\n')
432
434
433 def remove_pid_file(self):
435 def remove_pid_file(self):
434 """Remove the pid file.
436 """Remove the pid file.
435
437
436 This should be called at shutdown by registering a callback with
438 This should be called at shutdown by registering a callback with
437 :func:`reactor.addSystemEventTrigger`.
439 :func:`reactor.addSystemEventTrigger`.
438 """
440 """
439 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
441 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
440 if os.path.isfile(pid_file):
442 if os.path.isfile(pid_file):
441 try:
443 try:
442 self.log.info("Removing pid file: %s" % pid_file)
444 self.log.info("Removing pid file: %s" % pid_file)
443 os.remove(pid_file)
445 os.remove(pid_file)
444 except:
446 except:
445 pass
447 self.log.warn("Error removing the pid file: %s" % pid_file)
448 raise
446
449
447 def get_pid_from_file(self):
450 def get_pid_from_file(self):
448 """Get the pid from the pid file.
451 """Get the pid from the pid file.
449
452
450 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
453 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
451 """
454 """
452 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
455 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
453 if os.path.isfile(pid_file):
456 if os.path.isfile(pid_file):
454 with open(pid_file, 'r') as f:
457 with open(pid_file, 'r') as f:
455 pid = int(f.read().strip())
458 pid = int(f.read().strip())
456 return pid
459 return pid
457 else:
460 else:
458 raise PIDFileError('pid file not found: %s' % pid_file) No newline at end of file
461 raise PIDFileError('pid file not found: %s' % pid_file)
@@ -1,383 +1,384 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The ipcluster application.
4 The ipcluster application.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import logging
18 import logging
19 import os
19 import os
20 import signal
20 import signal
21 import sys
21 import sys
22
22
23 from twisted.scripts._twistd_unix import daemonize
23 if os.name=='posix':
24 from twisted.scripts._twistd_unix import daemonize
24
25
25 from IPython.core import release
26 from IPython.core import release
26 from IPython.external import argparse
27 from IPython.external import argparse
27 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
28 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
28 from IPython.utils.importstring import import_item
29 from IPython.utils.importstring import import_item
29
30
30 from IPython.kernel.clusterdir import (
31 from IPython.kernel.clusterdir import (
31 ApplicationWithClusterDir, ClusterDirError, PIDFileError
32 ApplicationWithClusterDir, ClusterDirError, PIDFileError
32 )
33 )
33
34
34 from twisted.internet import reactor, defer
35 from twisted.internet import reactor, defer
35 from twisted.python import log
36 from twisted.python import log
36
37
37 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
38 # Code for launchers
39 # Code for launchers
39 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
40
41
41
42
42
43
43 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
44 # The ipcluster application
45 # The ipcluster application
45 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
46
47
47
48
48 class IPClusterCLLoader(ArgParseConfigLoader):
49 class IPClusterCLLoader(ArgParseConfigLoader):
49
50
50 def _add_arguments(self):
51 def _add_arguments(self):
51 # This has all the common options that all subcommands use
52 # This has all the common options that all subcommands use
52 parent_parser1 = argparse.ArgumentParser(add_help=False)
53 parent_parser1 = argparse.ArgumentParser(add_help=False)
53 parent_parser1.add_argument('--ipython-dir',
54 parent_parser1.add_argument('--ipython-dir',
54 dest='Global.ipythondir',type=str,
55 dest='Global.ipythondir',type=str,
55 help='Set to override default location of Global.ipythondir.',
56 help='Set to override default location of Global.ipythondir.',
56 default=NoConfigDefault,
57 default=NoConfigDefault,
57 metavar='Global.ipythondir')
58 metavar='Global.ipythondir')
58 parent_parser1.add_argument('--log-level',
59 parent_parser1.add_argument('--log-level',
59 dest="Global.log_level",type=int,
60 dest="Global.log_level",type=int,
60 help='Set the log level (0,10,20,30,40,50). Default is 30.',
61 help='Set the log level (0,10,20,30,40,50). Default is 30.',
61 default=NoConfigDefault,
62 default=NoConfigDefault,
62 metavar='Global.log_level')
63 metavar='Global.log_level')
63
64
64 # This has all the common options that other subcommands use
65 # This has all the common options that other subcommands use
65 parent_parser2 = argparse.ArgumentParser(add_help=False)
66 parent_parser2 = argparse.ArgumentParser(add_help=False)
66 parent_parser2.add_argument('-p','--profile',
67 parent_parser2.add_argument('-p','--profile',
67 dest='Global.profile',type=str,
68 dest='Global.profile',type=str,
68 default=NoConfigDefault,
69 default=NoConfigDefault,
69 help='The string name of the profile to be used. This determines '
70 help='The string name of the profile to be used. This determines '
70 'the name of the cluster dir as: cluster_<profile>. The default profile '
71 'the name of the cluster dir as: cluster_<profile>. The default profile '
71 'is named "default". The cluster directory is resolve this way '
72 'is named "default". The cluster directory is resolve this way '
72 'if the --cluster-dir option is not used.',
73 'if the --cluster-dir option is not used.',
73 default=NoConfigDefault,
74 default=NoConfigDefault,
74 metavar='Global.profile')
75 metavar='Global.profile')
75 parent_parser2.add_argument('--cluster-dir',
76 parent_parser2.add_argument('--cluster-dir',
76 dest='Global.cluster_dir',type=str,
77 dest='Global.cluster_dir',type=str,
77 default=NoConfigDefault,
78 default=NoConfigDefault,
78 help='Set the cluster dir. This overrides the logic used by the '
79 help='Set the cluster dir. This overrides the logic used by the '
79 '--profile option.',
80 '--profile option.',
80 default=NoConfigDefault,
81 default=NoConfigDefault,
81 metavar='Global.cluster_dir')
82 metavar='Global.cluster_dir')
82 parent_parser2.add_argument('--log-to-file',
83 parent_parser2.add_argument('--log-to-file',
83 action='store_true', dest='Global.log_to_file',
84 action='store_true', dest='Global.log_to_file',
84 default=NoConfigDefault,
85 default=NoConfigDefault,
85 help='Log to a file in the log directory (default is stdout)'
86 help='Log to a file in the log directory (default is stdout)'
86 )
87 )
87
88
88 subparsers = self.parser.add_subparsers(
89 subparsers = self.parser.add_subparsers(
89 dest='Global.subcommand',
90 dest='Global.subcommand',
90 title='ipcluster subcommands',
91 title='ipcluster subcommands',
91 description='ipcluster has a variety of subcommands. '
92 description='ipcluster has a variety of subcommands. '
92 'The general way of running ipcluster is "ipcluster <cmd> '
93 'The general way of running ipcluster is "ipcluster <cmd> '
93 ' [options]""',
94 ' [options]""',
94 help='For more help, type "ipcluster <cmd> -h"')
95 help='For more help, type "ipcluster <cmd> -h"')
95
96
96 parser_list = subparsers.add_parser(
97 parser_list = subparsers.add_parser(
97 'list',
98 'list',
98 help='List all clusters in cwd and ipythondir.',
99 help='List all clusters in cwd and ipythondir.',
99 parents=[parent_parser1]
100 parents=[parent_parser1]
100 )
101 )
101
102
102 parser_create = subparsers.add_parser(
103 parser_create = subparsers.add_parser(
103 'create',
104 'create',
104 help='Create a new cluster directory.',
105 help='Create a new cluster directory.',
105 parents=[parent_parser1, parent_parser2]
106 parents=[parent_parser1, parent_parser2]
106 )
107 )
107 parser_create.add_argument(
108 parser_create.add_argument(
108 '--reset-config',
109 '--reset-config',
109 dest='Global.reset_config', action='store_true',
110 dest='Global.reset_config', action='store_true',
110 default=NoConfigDefault,
111 default=NoConfigDefault,
111 help='Recopy the default config files to the cluster directory. '
112 help='Recopy the default config files to the cluster directory. '
112 'You will loose any modifications you have made to these files.'
113 'You will loose any modifications you have made to these files.'
113 )
114 )
114
115
115 parser_start = subparsers.add_parser(
116 parser_start = subparsers.add_parser(
116 'start',
117 'start',
117 help='Start a cluster.',
118 help='Start a cluster.',
118 parents=[parent_parser1, parent_parser2]
119 parents=[parent_parser1, parent_parser2]
119 )
120 )
120 parser_start.add_argument(
121 parser_start.add_argument(
121 '-n', '--number',
122 '-n', '--number',
122 type=int, dest='Global.n',
123 type=int, dest='Global.n',
123 default=NoConfigDefault,
124 default=NoConfigDefault,
124 help='The number of engines to start.',
125 help='The number of engines to start.',
125 metavar='Global.n'
126 metavar='Global.n'
126 )
127 )
127 parser_start.add_argument('--clean-logs',
128 parser_start.add_argument('--clean-logs',
128 dest='Global.clean_logs', action='store_true',
129 dest='Global.clean_logs', action='store_true',
129 help='Delete old log flies before starting.',
130 help='Delete old log flies before starting.',
130 default=NoConfigDefault
131 default=NoConfigDefault
131 )
132 )
132 parser_start.add_argument('--no-clean-logs',
133 parser_start.add_argument('--no-clean-logs',
133 dest='Global.clean_logs', action='store_false',
134 dest='Global.clean_logs', action='store_false',
134 help="Don't delete old log flies before starting.",
135 help="Don't delete old log flies before starting.",
135 default=NoConfigDefault
136 default=NoConfigDefault
136 )
137 )
137 parser_start.add_argument('--daemon',
138 parser_start.add_argument('--daemon',
138 dest='Global.daemonize', action='store_true',
139 dest='Global.daemonize', action='store_true',
139 help='Daemonize the ipcluster program. This implies --log-to-file',
140 help='Daemonize the ipcluster program. This implies --log-to-file',
140 default=NoConfigDefault
141 default=NoConfigDefault
141 )
142 )
142 parser_start.add_argument('--nodaemon',
143 parser_start.add_argument('--nodaemon',
143 dest='Global.daemonize', action='store_false',
144 dest='Global.daemonize', action='store_false',
144 help="Dont't daemonize the ipcluster program.",
145 help="Dont't daemonize the ipcluster program.",
145 default=NoConfigDefault
146 default=NoConfigDefault
146 )
147 )
147
148
148 parser_start = subparsers.add_parser(
149 parser_start = subparsers.add_parser(
149 'stop',
150 'stop',
150 help='Stop a cluster.',
151 help='Stop a cluster.',
151 parents=[parent_parser1, parent_parser2]
152 parents=[parent_parser1, parent_parser2]
152 )
153 )
153 parser_start.add_argument('--signal-number',
154 parser_start.add_argument('--signal-number',
154 dest='Global.stop_signal', type=int,
155 dest='Global.stop_signal', type=int,
155 help="The signal number to use in stopping the cluster (default=2).",
156 help="The signal number to use in stopping the cluster (default=2).",
156 metavar="Global.stop_signal",
157 metavar="Global.stop_signal",
157 default=NoConfigDefault
158 default=NoConfigDefault
158 )
159 )
159
160
160 default_config_file_name = 'ipcluster_config.py'
161 default_config_file_name = 'ipcluster_config.py'
161
162
162
163
163 class IPClusterApp(ApplicationWithClusterDir):
164 class IPClusterApp(ApplicationWithClusterDir):
164
165
165 name = 'ipcluster'
166 name = 'ipcluster'
166 description = 'Start an IPython cluster (controller and engines).'
167 description = 'Start an IPython cluster (controller and engines).'
167 config_file_name = default_config_file_name
168 config_file_name = default_config_file_name
168 default_log_level = logging.INFO
169 default_log_level = logging.INFO
169 auto_create_cluster_dir = False
170 auto_create_cluster_dir = False
170
171
171 def create_default_config(self):
172 def create_default_config(self):
172 super(IPClusterApp, self).create_default_config()
173 super(IPClusterApp, self).create_default_config()
173 self.default_config.Global.controller_launcher = \
174 self.default_config.Global.controller_launcher = \
174 'IPython.kernel.launcher.LocalControllerLauncher'
175 'IPython.kernel.launcher.LocalControllerLauncher'
175 self.default_config.Global.engine_launcher = \
176 self.default_config.Global.engine_launcher = \
176 'IPython.kernel.launcher.LocalEngineSetLauncher'
177 'IPython.kernel.launcher.LocalEngineSetLauncher'
177 self.default_config.Global.n = 2
178 self.default_config.Global.n = 2
178 self.default_config.Global.reset_config = False
179 self.default_config.Global.reset_config = False
179 self.default_config.Global.clean_logs = True
180 self.default_config.Global.clean_logs = True
180 self.default_config.Global.stop_signal = 2
181 self.default_config.Global.stop_signal = 2
181 self.default_config.Global.daemonize = False
182 self.default_config.Global.daemonize = False
182
183
183 def create_command_line_config(self):
184 def create_command_line_config(self):
184 """Create and return a command line config loader."""
185 """Create and return a command line config loader."""
185 return IPClusterCLLoader(
186 return IPClusterCLLoader(
186 description=self.description,
187 description=self.description,
187 version=release.version
188 version=release.version
188 )
189 )
189
190
190 def find_resources(self):
191 def find_resources(self):
191 subcommand = self.command_line_config.Global.subcommand
192 subcommand = self.command_line_config.Global.subcommand
192 if subcommand=='list':
193 if subcommand=='list':
193 self.list_cluster_dirs()
194 self.list_cluster_dirs()
194 # Exit immediately because there is nothing left to do.
195 # Exit immediately because there is nothing left to do.
195 self.exit()
196 self.exit()
196 elif subcommand=='create':
197 elif subcommand=='create':
197 self.auto_create_cluster_dir = True
198 self.auto_create_cluster_dir = True
198 super(IPClusterApp, self).find_resources()
199 super(IPClusterApp, self).find_resources()
199 elif subcommand=='start' or subcommand=='stop':
200 elif subcommand=='start' or subcommand=='stop':
200 self.auto_create_cluster_dir = False
201 self.auto_create_cluster_dir = False
201 try:
202 try:
202 super(IPClusterApp, self).find_resources()
203 super(IPClusterApp, self).find_resources()
203 except ClusterDirError:
204 except ClusterDirError:
204 raise ClusterDirError(
205 raise ClusterDirError(
205 "Could not find a cluster directory. A cluster dir must "
206 "Could not find a cluster directory. A cluster dir must "
206 "be created before running 'ipcluster start'. Do "
207 "be created before running 'ipcluster start'. Do "
207 "'ipcluster create -h' or 'ipcluster list -h' for more "
208 "'ipcluster create -h' or 'ipcluster list -h' for more "
208 "information about creating and listing cluster dirs."
209 "information about creating and listing cluster dirs."
209 )
210 )
210
211
211 def pre_construct(self):
212 def pre_construct(self):
212 super(IPClusterApp, self).pre_construct()
213 super(IPClusterApp, self).pre_construct()
213 config = self.master_config
214 config = self.master_config
214 try:
215 try:
215 daemon = config.Global.daemonize
216 daemon = config.Global.daemonize
216 if daemon:
217 if daemon:
217 config.Global.log_to_file = True
218 config.Global.log_to_file = True
218 except AttributeError:
219 except AttributeError:
219 pass
220 pass
220
221
221 def construct(self):
222 def construct(self):
222 config = self.master_config
223 config = self.master_config
223 if config.Global.subcommand=='list':
224 if config.Global.subcommand=='list':
224 pass
225 pass
225 elif config.Global.subcommand=='create':
226 elif config.Global.subcommand=='create':
226 self.log.info('Copying default config files to cluster directory '
227 self.log.info('Copying default config files to cluster directory '
227 '[overwrite=%r]' % (config.Global.reset_config,))
228 '[overwrite=%r]' % (config.Global.reset_config,))
228 self.cluster_dir_obj.copy_all_config_files(overwrite=config.Global.reset_config)
229 self.cluster_dir_obj.copy_all_config_files(overwrite=config.Global.reset_config)
229 elif config.Global.subcommand=='start':
230 elif config.Global.subcommand=='start':
230 self.start_logging()
231 self.start_logging()
231 reactor.callWhenRunning(self.start_launchers)
232 reactor.callWhenRunning(self.start_launchers)
232
233
233 def list_cluster_dirs(self):
234 def list_cluster_dirs(self):
234 # Find the search paths
235 # Find the search paths
235 cluster_dir_paths = os.environ.get('IPCLUSTERDIR_PATH','')
236 cluster_dir_paths = os.environ.get('IPCLUSTERDIR_PATH','')
236 if cluster_dir_paths:
237 if cluster_dir_paths:
237 cluster_dir_paths = cluster_dir_paths.split(':')
238 cluster_dir_paths = cluster_dir_paths.split(':')
238 else:
239 else:
239 cluster_dir_paths = []
240 cluster_dir_paths = []
240 try:
241 try:
241 ipythondir = self.command_line_config.Global.ipythondir
242 ipythondir = self.command_line_config.Global.ipythondir
242 except AttributeError:
243 except AttributeError:
243 ipythondir = self.default_config.Global.ipythondir
244 ipythondir = self.default_config.Global.ipythondir
244 paths = [os.getcwd(), ipythondir] + \
245 paths = [os.getcwd(), ipythondir] + \
245 cluster_dir_paths
246 cluster_dir_paths
246 paths = list(set(paths))
247 paths = list(set(paths))
247
248
248 self.log.info('Searching for cluster dirs in paths: %r' % paths)
249 self.log.info('Searching for cluster dirs in paths: %r' % paths)
249 for path in paths:
250 for path in paths:
250 files = os.listdir(path)
251 files = os.listdir(path)
251 for f in files:
252 for f in files:
252 full_path = os.path.join(path, f)
253 full_path = os.path.join(path, f)
253 if os.path.isdir(full_path) and f.startswith('cluster_'):
254 if os.path.isdir(full_path) and f.startswith('cluster_'):
254 profile = full_path.split('_')[-1]
255 profile = full_path.split('_')[-1]
255 start_cmd = '"ipcluster start -n 4 -p %s"' % profile
256 start_cmd = '"ipcluster start -n 4 -p %s"' % profile
256 print start_cmd + " ==> " + full_path
257 print start_cmd + " ==> " + full_path
257
258
258 def start_launchers(self):
259 def start_launchers(self):
259 config = self.master_config
260 config = self.master_config
260
261
261 # Create the launchers
262 # Create the launchers
262 el_class = import_item(config.Global.engine_launcher)
263 el_class = import_item(config.Global.engine_launcher)
263 self.engine_launcher = el_class(
264 self.engine_launcher = el_class(
264 self.cluster_dir, config=config
265 self.cluster_dir, config=config
265 )
266 )
266 cl_class = import_item(config.Global.controller_launcher)
267 cl_class = import_item(config.Global.controller_launcher)
267 self.controller_launcher = cl_class(
268 self.controller_launcher = cl_class(
268 self.cluster_dir, config=config
269 self.cluster_dir, config=config
269 )
270 )
270
271
271 # Setup signals
272 # Setup signals
272 signal.signal(signal.SIGINT, self.stop_launchers)
273 signal.signal(signal.SIGINT, self.stop_launchers)
273 # signal.signal(signal.SIGKILL, self.stop_launchers)
274 # signal.signal(signal.SIGKILL, self.stop_launchers)
274
275
275 # Setup the observing of stopping
276 # Setup the observing of stopping
276 d1 = self.controller_launcher.observe_stop()
277 d1 = self.controller_launcher.observe_stop()
277 d1.addCallback(self.stop_engines)
278 d1.addCallback(self.stop_engines)
278 d1.addErrback(self.err_and_stop)
279 d1.addErrback(self.err_and_stop)
279 # If this triggers, just let them die
280 # If this triggers, just let them die
280 # d2 = self.engine_launcher.observe_stop()
281 # d2 = self.engine_launcher.observe_stop()
281
282
282 # Start the controller and engines
283 # Start the controller and engines
283 d = self.controller_launcher.start(
284 d = self.controller_launcher.start(
284 profile=None, cluster_dir=config.Global.cluster_dir
285 profile=None, cluster_dir=config.Global.cluster_dir
285 )
286 )
286 d.addCallback(lambda _: self.start_engines())
287 d.addCallback(lambda _: self.start_engines())
287 d.addErrback(self.err_and_stop)
288 d.addErrback(self.err_and_stop)
288
289
289 def err_and_stop(self, f):
290 def err_and_stop(self, f):
290 log.msg('Unexpected error in ipcluster:')
291 log.msg('Unexpected error in ipcluster:')
291 log.err(f)
292 log.err(f)
292 reactor.stop()
293 reactor.stop()
293
294
294 def stop_engines(self, r):
295 def stop_engines(self, r):
295 return self.engine_launcher.stop()
296 return self.engine_launcher.stop()
296
297
297 def start_engines(self):
298 def start_engines(self):
298 config = self.master_config
299 config = self.master_config
299 d = self.engine_launcher.start(
300 d = self.engine_launcher.start(
300 config.Global.n,
301 config.Global.n,
301 profile=None, cluster_dir=config.Global.cluster_dir
302 profile=None, cluster_dir=config.Global.cluster_dir
302 )
303 )
303 return d
304 return d
304
305
305 def stop_launchers(self, signum, frame):
306 def stop_launchers(self, signum, frame):
306 log.msg("Stopping cluster")
307 log.msg("Stopping cluster")
307 d1 = self.engine_launcher.stop()
308 d1 = self.engine_launcher.stop()
308 d2 = self.controller_launcher.stop()
309 d2 = self.controller_launcher.stop()
309 # d1.addCallback(lambda _: self.controller_launcher.stop)
310 # d1.addCallback(lambda _: self.controller_launcher.stop)
310 d1.addErrback(self.err_and_stop)
311 d1.addErrback(self.err_and_stop)
311 d2.addErrback(self.err_and_stop)
312 d2.addErrback(self.err_and_stop)
312 reactor.callLater(2.0, reactor.stop)
313 reactor.callLater(2.0, reactor.stop)
313
314
314 def start_logging(self):
315 def start_logging(self):
315 # Remove old log files
316 # Remove old log files
316 if self.master_config.Global.clean_logs:
317 if self.master_config.Global.clean_logs:
317 log_dir = self.master_config.Global.log_dir
318 log_dir = self.master_config.Global.log_dir
318 for f in os.listdir(log_dir):
319 for f in os.listdir(log_dir):
319 if f.startswith('ipengine' + '-') and f.endswith('.log'):
320 if f.startswith('ipengine' + '-') and f.endswith('.log'):
320 os.remove(os.path.join(log_dir, f))
321 os.remove(os.path.join(log_dir, f))
321 for f in os.listdir(log_dir):
322 for f in os.listdir(log_dir):
322 if f.startswith('ipcontroller' + '-') and f.endswith('.log'):
323 if f.startswith('ipcontroller' + '-') and f.endswith('.log'):
323 os.remove(os.path.join(log_dir, f))
324 os.remove(os.path.join(log_dir, f))
324 super(IPClusterApp, self).start_logging()
325 super(IPClusterApp, self).start_logging()
325
326
326 def start_app(self):
327 def start_app(self):
327 """Start the application, depending on what subcommand is used."""
328 """Start the application, depending on what subcommand is used."""
328 config = self.master_config
329 config = self.master_config
329 subcmd = config.Global.subcommand
330 subcmd = config.Global.subcommand
330 if subcmd=='create' or subcmd=='list':
331 if subcmd=='create' or subcmd=='list':
331 return
332 return
332 elif subcmd=='start':
333 elif subcmd=='start':
333 # First see if the cluster is already running
334 # First see if the cluster is already running
334 try:
335 try:
335 pid = self.get_pid_from_file()
336 pid = self.get_pid_from_file()
336 except:
337 except:
337 pass
338 pass
338 else:
339 else:
339 self.log.critical(
340 self.log.critical(
340 'Cluster is already running with [pid=%s]. '
341 'Cluster is already running with [pid=%s]. '
341 'use "ipcluster stop" to stop the cluster.' % pid
342 'use "ipcluster stop" to stop the cluster.' % pid
342 )
343 )
343 # Here I exit with a unusual exit status that other processes
344 # Here I exit with a unusual exit status that other processes
344 # can watch for to learn how I existed.
345 # can watch for to learn how I existed.
345 sys.exit(10)
346 sys.exit(10)
346 # Now log and daemonize
347 # Now log and daemonize
347 self.log.info('Starting ipcluster with [daemon=%r]' % config.Global.daemonize)
348 self.log.info('Starting ipcluster with [daemon=%r]' % config.Global.daemonize)
348 if config.Global.daemonize:
349 if config.Global.daemonize:
349 if os.name=='posix':
350 if os.name=='posix':
350 os.chdir(config.Global.cluster_dir)
351 os.chdir(config.Global.cluster_dir)
351 self.log_level = 40
352 self.log_level = 40
352 daemonize()
353 daemonize()
353
354
354 # Now write the new pid file after our new forked pid is active.
355 # Now write the new pid file after our new forked pid is active.
355 self.write_pid_file()
356 self.write_pid_file()
356 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
357 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
357 reactor.run()
358 reactor.run()
358 elif subcmd=='stop':
359 elif subcmd=='stop':
359 try:
360 try:
360 pid = self.get_pid_from_file()
361 pid = self.get_pid_from_file()
361 except PIDFileError:
362 except PIDFileError:
362 self.log.critical(
363 self.log.critical(
363 'Problem reading pid file, cluster is probably not running.'
364 'Problem reading pid file, cluster is probably not running.'
364 )
365 )
365 # Here I exit with a unusual exit status that other processes
366 # Here I exit with a unusual exit status that other processes
366 # can watch for to learn how I existed.
367 # can watch for to learn how I existed.
367 sys.exit(11)
368 sys.exit(11)
368 sig = config.Global.stop_signal
369 sig = config.Global.stop_signal
369 self.log.info(
370 self.log.info(
370 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
371 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
371 )
372 )
372 os.kill(pid, sig)
373 os.kill(pid, sig)
373
374
374
375
375 def launch_new_instance():
376 def launch_new_instance():
376 """Create and run the IPython cluster."""
377 """Create and run the IPython cluster."""
377 app = IPClusterApp()
378 app = IPClusterApp()
378 app.start()
379 app.start()
379
380
380
381
381 if __name__ == '__main__':
382 if __name__ == '__main__':
382 launch_new_instance()
383 launch_new_instance()
383
384
@@ -1,262 +1,262 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython controller application.
4 The IPython controller application.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import copy
20 import copy
21 import os
21 import os
22 import sys
22 import sys
23
23
24 from twisted.application import service
24 from twisted.application import service
25 from twisted.internet import reactor
25 from twisted.internet import reactor
26 from twisted.python import log
26 from twisted.python import log
27
27
28 from IPython.config.loader import Config, NoConfigDefault
28 from IPython.config.loader import Config, NoConfigDefault
29
29
30 from IPython.kernel.clusterdir import (
30 from IPython.kernel.clusterdir import (
31 ApplicationWithClusterDir,
31 ApplicationWithClusterDir,
32 AppWithClusterDirArgParseConfigLoader
32 AppWithClusterDirArgParseConfigLoader
33 )
33 )
34
34
35 from IPython.core import release
35 from IPython.core import release
36
36
37 from IPython.utils.traitlets import Str, Instance
37 from IPython.utils.traitlets import Str, Instance
38
38
39 from IPython.kernel import controllerservice
39 from IPython.kernel import controllerservice
40
40
41 from IPython.kernel.fcutil import FCServiceFactory
41 from IPython.kernel.fcutil import FCServiceFactory
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Default interfaces
44 # Default interfaces
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47
47
48 # The default client interfaces for FCClientServiceFactory.interfaces
48 # The default client interfaces for FCClientServiceFactory.interfaces
49 default_client_interfaces = Config()
49 default_client_interfaces = Config()
50 default_client_interfaces.Task.interface_chain = [
50 default_client_interfaces.Task.interface_chain = [
51 'IPython.kernel.task.ITaskController',
51 'IPython.kernel.task.ITaskController',
52 'IPython.kernel.taskfc.IFCTaskController'
52 'IPython.kernel.taskfc.IFCTaskController'
53 ]
53 ]
54
54
55 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
55 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
56
56
57 default_client_interfaces.MultiEngine.interface_chain = [
57 default_client_interfaces.MultiEngine.interface_chain = [
58 'IPython.kernel.multiengine.IMultiEngine',
58 'IPython.kernel.multiengine.IMultiEngine',
59 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
59 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
60 ]
60 ]
61
61
62 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
62 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
63
63
64 # Make this a dict we can pass to Config.__init__ for the default
64 # Make this a dict we can pass to Config.__init__ for the default
65 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
65 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
66
66
67
67
68
68
69 # The default engine interfaces for FCEngineServiceFactory.interfaces
69 # The default engine interfaces for FCEngineServiceFactory.interfaces
70 default_engine_interfaces = Config()
70 default_engine_interfaces = Config()
71 default_engine_interfaces.Default.interface_chain = [
71 default_engine_interfaces.Default.interface_chain = [
72 'IPython.kernel.enginefc.IFCControllerBase'
72 'IPython.kernel.enginefc.IFCControllerBase'
73 ]
73 ]
74
74
75 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
75 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
76
76
77 # Make this a dict we can pass to Config.__init__ for the default
77 # Make this a dict we can pass to Config.__init__ for the default
78 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
78 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
79
79
80
80
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 # Service factories
82 # Service factories
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84
84
85
85
86 class FCClientServiceFactory(FCServiceFactory):
86 class FCClientServiceFactory(FCServiceFactory):
87 """A Foolscap implementation of the client services."""
87 """A Foolscap implementation of the client services."""
88
88
89 cert_file = Str('ipcontroller-client.pem', config=True)
89 cert_file = Str('ipcontroller-client.pem', config=True)
90 interfaces = Instance(klass=Config, kw=default_client_interfaces,
90 interfaces = Instance(klass=Config, kw=default_client_interfaces,
91 allow_none=False, config=True)
91 allow_none=False, config=True)
92
92
93
93
94 class FCEngineServiceFactory(FCServiceFactory):
94 class FCEngineServiceFactory(FCServiceFactory):
95 """A Foolscap implementation of the engine services."""
95 """A Foolscap implementation of the engine services."""
96
96
97 cert_file = Str('ipcontroller-engine.pem', config=True)
97 cert_file = Str('ipcontroller-engine.pem', config=True)
98 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
98 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
99 allow_none=False, config=True)
99 allow_none=False, config=True)
100
100
101
101
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103 # The main application
103 # The main application
104 #-----------------------------------------------------------------------------
104 #-----------------------------------------------------------------------------
105
105
106
106
107 cl_args = (
107 cl_args = (
108 # Client config
108 # Client config
109 (('--client-ip',), dict(
109 (('--client-ip',), dict(
110 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
110 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
111 help='The IP address or hostname the controller will listen on for '
111 help='The IP address or hostname the controller will listen on for '
112 'client connections.',
112 'client connections.',
113 metavar='FCClientServiceFactory.ip')
113 metavar='FCClientServiceFactory.ip')
114 ),
114 ),
115 (('--client-port',), dict(
115 (('--client-port',), dict(
116 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
116 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
117 help='The port the controller will listen on for client connections. '
117 help='The port the controller will listen on for client connections. '
118 'The default is to use 0, which will autoselect an open port.',
118 'The default is to use 0, which will autoselect an open port.',
119 metavar='FCClientServiceFactory.port')
119 metavar='FCClientServiceFactory.port')
120 ),
120 ),
121 (('--client-location',), dict(
121 (('--client-location',), dict(
122 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
122 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
123 help='The hostname or IP that clients should connect to. This does '
123 help='The hostname or IP that clients should connect to. This does '
124 'not control which interface the controller listens on. Instead, this '
124 'not control which interface the controller listens on. Instead, this '
125 'determines the hostname/IP that is listed in the FURL, which is how '
125 'determines the hostname/IP that is listed in the FURL, which is how '
126 'clients know where to connect. Useful if the controller is listening '
126 'clients know where to connect. Useful if the controller is listening '
127 'on multiple interfaces.',
127 'on multiple interfaces.',
128 metavar='FCClientServiceFactory.location')
128 metavar='FCClientServiceFactory.location')
129 ),
129 ),
130 # Engine config
130 # Engine config
131 (('--engine-ip',), dict(
131 (('--engine-ip',), dict(
132 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
132 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
133 help='The IP address or hostname the controller will listen on for '
133 help='The IP address or hostname the controller will listen on for '
134 'engine connections.',
134 'engine connections.',
135 metavar='FCEngineServiceFactory.ip')
135 metavar='FCEngineServiceFactory.ip')
136 ),
136 ),
137 (('--engine-port',), dict(
137 (('--engine-port',), dict(
138 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
138 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
139 help='The port the controller will listen on for engine connections. '
139 help='The port the controller will listen on for engine connections. '
140 'The default is to use 0, which will autoselect an open port.',
140 'The default is to use 0, which will autoselect an open port.',
141 metavar='FCEngineServiceFactory.port')
141 metavar='FCEngineServiceFactory.port')
142 ),
142 ),
143 (('--engine-location',), dict(
143 (('--engine-location',), dict(
144 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
144 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
145 help='The hostname or IP that engines should connect to. This does '
145 help='The hostname or IP that engines should connect to. This does '
146 'not control which interface the controller listens on. Instead, this '
146 'not control which interface the controller listens on. Instead, this '
147 'determines the hostname/IP that is listed in the FURL, which is how '
147 'determines the hostname/IP that is listed in the FURL, which is how '
148 'engines know where to connect. Useful if the controller is listening '
148 'engines know where to connect. Useful if the controller is listening '
149 'on multiple interfaces.',
149 'on multiple interfaces.',
150 metavar='FCEngineServiceFactory.location')
150 metavar='FCEngineServiceFactory.location')
151 ),
151 ),
152 # Global config
152 # Global config
153 (('--log-to-file',), dict(
153 (('--log-to-file',), dict(
154 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
154 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
155 help='Log to a file in the log directory (default is stdout)')
155 help='Log to a file in the log directory (default is stdout)')
156 ),
156 ),
157 (('-r','--reuse-furls'), dict(
157 (('-r','--reuse-furls'), dict(
158 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
158 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
159 help='Try to reuse all FURL files. If this is not set all FURL files '
159 help='Try to reuse all FURL files. If this is not set all FURL files '
160 'are deleted before the controller starts. This must be set if '
160 'are deleted before the controller starts. This must be set if '
161 'specific ports are specified by --engine-port or --client-port.')
161 'specific ports are specified by --engine-port or --client-port.')
162 ),
162 ),
163 (('--no-secure',), dict(
163 (('--no-secure',), dict(
164 action='store_false', dest='Global.secure', default=NoConfigDefault,
164 action='store_false', dest='Global.secure', default=NoConfigDefault,
165 help='Turn off SSL encryption for all connections.')
165 help='Turn off SSL encryption for all connections.')
166 ),
166 ),
167 (('--secure',), dict(
167 (('--secure',), dict(
168 action='store_true', dest='Global.secure', default=NoConfigDefault,
168 action='store_true', dest='Global.secure', default=NoConfigDefault,
169 help='Turn off SSL encryption for all connections.')
169 help='Turn off SSL encryption for all connections.')
170 )
170 )
171 )
171 )
172
172
173
173
174 class IPControllerAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
174 class IPControllerAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
175
175
176 arguments = cl_args
176 arguments = cl_args
177
177
178
178
179 default_config_file_name = 'ipcontroller_config.py'
179 default_config_file_name = 'ipcontroller_config.py'
180
180
181
181
182 class IPControllerApp(ApplicationWithClusterDir):
182 class IPControllerApp(ApplicationWithClusterDir):
183
183
184 name = 'ipcontroller'
184 name = 'ipcontroller'
185 description = 'Start the IPython controller for parallel computing.'
185 description = 'Start the IPython controller for parallel computing.'
186 config_file_name = default_config_file_name
186 config_file_name = default_config_file_name
187 auto_create_cluster_dir = True
187 auto_create_cluster_dir = True
188
188
189 def create_default_config(self):
189 def create_default_config(self):
190 super(IPControllerApp, self).create_default_config()
190 super(IPControllerApp, self).create_default_config()
191 self.default_config.Global.reuse_furls = False
191 self.default_config.Global.reuse_furls = False
192 self.default_config.Global.secure = True
192 self.default_config.Global.secure = True
193 self.default_config.Global.import_statements = []
193 self.default_config.Global.import_statements = []
194 self.default_config.Global.clean_logs = True
194 self.default_config.Global.clean_logs = True
195
195
196 def create_command_line_config(self):
196 def create_command_line_config(self):
197 """Create and return a command line config loader."""
197 """Create and return a command line config loader."""
198 return IPControllerAppCLConfigLoader(
198 return IPControllerAppCLConfigLoader(
199 description=self.description,
199 description=self.description,
200 version=release.version
200 version=release.version
201 )
201 )
202
202
203 def post_load_command_line_config(self):
203 def post_load_command_line_config(self):
204 # Now setup reuse_furls
204 # Now setup reuse_furls
205 c = self.command_line_config
205 c = self.command_line_config
206 if hasattr(c.Global, 'reuse_furls'):
206 if hasattr(c.Global, 'reuse_furls'):
207 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
207 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
208 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
208 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
209 del c.Global.reuse_furls
209 del c.Global.reuse_furls
210 if hasattr(c.Global, 'secure'):
210 if hasattr(c.Global, 'secure'):
211 c.FCClientServiceFactory.secure = c.Global.secure
211 c.FCClientServiceFactory.secure = c.Global.secure
212 c.FCEngineServiceFactory.secure = c.Global.secure
212 c.FCEngineServiceFactory.secure = c.Global.secure
213 del c.Global.secure
213 del c.Global.secure
214
214
215 def construct(self):
215 def construct(self):
216 # I am a little hesitant to put these into InteractiveShell itself.
216 # I am a little hesitant to put these into InteractiveShell itself.
217 # But that might be the place for them
217 # But that might be the place for them
218 sys.path.insert(0, '')
218 sys.path.insert(0, '')
219
219
220 self.start_logging()
220 self.start_logging()
221 self.import_statements()
221 self.import_statements()
222
222
223 # Create the service hierarchy
223 # Create the service hierarchy
224 self.main_service = service.MultiService()
224 self.main_service = service.MultiService()
225 # The controller service
225 # The controller service
226 controller_service = controllerservice.ControllerService()
226 controller_service = controllerservice.ControllerService()
227 controller_service.setServiceParent(self.main_service)
227 controller_service.setServiceParent(self.main_service)
228 # The client tub and all its refereceables
228 # The client tub and all its refereceables
229 csfactory = FCClientServiceFactory(self.master_config, controller_service)
229 csfactory = FCClientServiceFactory(self.master_config, controller_service)
230 client_service = csfactory.create()
230 client_service = csfactory.create()
231 client_service.setServiceParent(self.main_service)
231 client_service.setServiceParent(self.main_service)
232 # The engine tub
232 # The engine tub
233 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
233 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
234 engine_service = esfactory.create()
234 engine_service = esfactory.create()
235 engine_service.setServiceParent(self.main_service)
235 engine_service.setServiceParent(self.main_service)
236
236
237 def import_statements(self):
237 def import_statements(self):
238 statements = self.master_config.Global.import_statements
238 statements = self.master_config.Global.import_statements
239 for s in statements:
239 for s in statements:
240 try:
240 try:
241 log.msg("Executing statement: '%s'" % s)
241 log.msg("Executing statement: '%s'" % s)
242 exec s in globals(), locals()
242 exec s in globals(), locals()
243 except:
243 except:
244 log.msg("Error running statement: %s" % s)
244 log.msg("Error running statement: %s" % s)
245
245
246 def start_app(self):
246 def start_app(self):
247 # Start the controller service and set things running
247 # Start the controller service and set things running
248 self.main_service.startService()
248 self.main_service.startService()
249 self.write_pid_file()
249 self.write_pid_file(overwrite=True)
250 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
250 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
251 reactor.run()
251 reactor.run()
252
252
253
253
254 def launch_new_instance():
254 def launch_new_instance():
255 """Create and run the IPython controller"""
255 """Create and run the IPython controller"""
256 app = IPControllerApp()
256 app = IPControllerApp()
257 app.start()
257 app.start()
258
258
259
259
260 if __name__ == '__main__':
260 if __name__ == '__main__':
261 launch_new_instance()
261 launch_new_instance()
262
262
General Comments 0
You need to be logged in to leave comments. Login now