##// END OF EJS Templates
Lots more work on the kernel scripts.
Brian Granger -
Show More
@@ -1,32 +1,24 b''
1 c = get_config()
1 c = get_config()
2
2
3 c.MPI.use = ''
3 # c.Global.log_to_file = False
4 # c.Global.exec_lines = ['import numpy']
5 # c.Global.log_dir_name = 'log'
6 # c.Global.security_dir_name = 'security'
7 # c.Global.log_level = 10
8 # c.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
9 # c.Global.furl_file_name = 'ipcontroller-engine.furl'
10 # c.Global.furl_file = ''
11
12 # c.MPI.use = ''
13 # c.MPI.mpi4py = """from mpi4py import MPI as mpi
14 # mpi.size = mpi.COMM_WORLD.Get_size()
15 # mpi.rank = mpi.COMM_WORLD.Get_rank()
16 # """
17 # c.MPI.pytrilinos = """from PyTrilinos import Epetra
18 # class SimpleStruct:
19 # pass
20 # mpi = SimpleStruct()
21 # mpi.rank = 0
22 # mpi.size = 0
23 # """
4
24
5 c.MPI.mpi4py = """from mpi4py import MPI as mpi
6 mpi.size = mpi.COMM_WORLD.Get_size()
7 mpi.rank = mpi.COMM_WORLD.Get_rank()
8 """
9
10 c.MPI.pytrilinos = """from PyTrilinos import Epetra
11 class SimpleStruct:
12 pass
13 mpi = SimpleStruct()
14 mpi.rank = 0
15 mpi.size = 0
16 """
17
18 c.Global.log_to_file = False
19
20 c.Global.exec_lines = ['import numpy']
21
22 c.Global.log_dir_name = 'log'
23
24 c.Global.security_dir_name = 'security'
25
26 c.Global.log_level = 10
27
28 c.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
29
30 c.Global.furl_file_name = 'ipcontroller-engine.furl'
31
32 c.Global.furl_file = ''
@@ -1,345 +1,364 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 An application for IPython.
4 An application for IPython.
5
5
6 All top-level applications should use the classes in this module for
6 All top-level applications should use the classes in this module for
7 handling configuration and creating componenets.
7 handling configuration and creating componenets.
8
8
9 The job of an :class:`Application` is to create the master configuration
9 The job of an :class:`Application` is to create the master configuration
10 object and then create the components, passing the config to them.
10 object and then create the components, passing the config to them.
11
11
12 Authors:
12 Authors:
13
13
14 * Brian Granger
14 * Brian Granger
15 * Fernando Perez
15 * Fernando Perez
16
16
17 Notes
17 Notes
18 -----
18 -----
19 """
19 """
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Copyright (C) 2008-2009 The IPython Development Team
22 # Copyright (C) 2008-2009 The IPython Development Team
23 #
23 #
24 # Distributed under the terms of the BSD License. The full license is in
24 # Distributed under the terms of the BSD License. The full license is in
25 # the file COPYING, distributed as part of this software.
25 # the file COPYING, distributed as part of this software.
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Imports
29 # Imports
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 import logging
32 import logging
33 import os
33 import os
34 import sys
34 import sys
35
35
36 from IPython.core import release
36 from IPython.core import release
37 from IPython.utils.genutils import get_ipython_dir
37 from IPython.utils.genutils import get_ipython_dir
38 from IPython.config.loader import (
38 from IPython.config.loader import (
39 PyFileConfigLoader,
39 PyFileConfigLoader,
40 ArgParseConfigLoader,
40 ArgParseConfigLoader,
41 Config,
41 Config,
42 NoConfigDefault
42 NoConfigDefault
43 )
43 )
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Classes and functions
46 # Classes and functions
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49
49
50 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
50 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
51 """Default command line options for IPython based applications."""
51 """Default command line options for IPython based applications."""
52
52
53 def _add_other_arguments(self):
53 def _add_other_arguments(self):
54 self.parser.add_argument('-ipythondir', '--ipython-dir',
54 self.parser.add_argument('-ipythondir', '--ipython-dir',
55 dest='Global.ipythondir',type=str,
55 dest='Global.ipythondir',type=str,
56 help='Set to override default location of Global.ipythondir.',
56 help='Set to override default location of Global.ipythondir.',
57 default=NoConfigDefault,
57 default=NoConfigDefault,
58 metavar='Global.ipythondir')
58 metavar='Global.ipythondir')
59 self.parser.add_argument('-p','-profile', '--profile',
59 self.parser.add_argument('-p','-profile', '--profile',
60 dest='Global.profile',type=str,
60 dest='Global.profile',type=str,
61 help='The string name of the ipython profile to be used.',
61 help='The string name of the ipython profile to be used.',
62 default=NoConfigDefault,
62 default=NoConfigDefault,
63 metavar='Global.profile')
63 metavar='Global.profile')
64 self.parser.add_argument('-log_level', '--log-level',
64 self.parser.add_argument('-log_level', '--log-level',
65 dest="Global.log_level",type=int,
65 dest="Global.log_level",type=int,
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
67 default=NoConfigDefault,
67 default=NoConfigDefault,
68 metavar='Global.log_level')
68 metavar='Global.log_level')
69 self.parser.add_argument('-config_file', '--config-file',
69 self.parser.add_argument('-config_file', '--config-file',
70 dest='Global.config_file',type=str,
70 dest='Global.config_file',type=str,
71 help='Set the config file name to override default.',
71 help='Set the config file name to override default.',
72 default=NoConfigDefault,
72 default=NoConfigDefault,
73 metavar='Global.config_file')
73 metavar='Global.config_file')
74
74
75
75
76 class ApplicationError(Exception):
76 class ApplicationError(Exception):
77 pass
77 pass
78
78
79
79
80 class Application(object):
80 class Application(object):
81 """Load a config, construct components and set them running."""
81 """Load a config, construct components and set them running."""
82
82
83 name = 'ipython'
83 name = 'ipython'
84 description = 'IPython: an enhanced interactive Python shell.'
84 description = 'IPython: an enhanced interactive Python shell.'
85 config_file_name = 'ipython_config.py'
85 config_file_name = 'ipython_config.py'
86 default_log_level = logging.WARN
86 default_log_level = logging.WARN
87
87
88 def __init__(self):
88 def __init__(self):
89 self._exiting = False
89 self.init_logger()
90 self.init_logger()
90 # Track the default and actual separately because some messages are
91 # Track the default and actual separately because some messages are
91 # only printed if we aren't using the default.
92 # only printed if we aren't using the default.
92 self.default_config_file_name = self.config_file_name
93 self.default_config_file_name = self.config_file_name
93
94
94 def init_logger(self):
95 def init_logger(self):
95 self.log = logging.getLogger(self.__class__.__name__)
96 self.log = logging.getLogger(self.__class__.__name__)
96 # This is used as the default until the command line arguments are read.
97 # This is used as the default until the command line arguments are read.
97 self.log.setLevel(self.default_log_level)
98 self.log.setLevel(self.default_log_level)
98 self._log_handler = logging.StreamHandler()
99 self._log_handler = logging.StreamHandler()
99 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
100 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
100 self._log_handler.setFormatter(self._log_formatter)
101 self._log_handler.setFormatter(self._log_formatter)
101 self.log.addHandler(self._log_handler)
102 self.log.addHandler(self._log_handler)
102
103
103 def _set_log_level(self, level):
104 def _set_log_level(self, level):
104 self.log.setLevel(level)
105 self.log.setLevel(level)
105
106
106 def _get_log_level(self):
107 def _get_log_level(self):
107 return self.log.level
108 return self.log.level
108
109
109 log_level = property(_get_log_level, _set_log_level)
110 log_level = property(_get_log_level, _set_log_level)
110
111
111 def start(self):
112 def start(self):
112 """Start the application."""
113 """Start the application."""
113 self.attempt(self.create_default_config)
114 self.attempt(self.create_default_config)
114 self.log_default_config()
115 self.log_default_config()
115 self.set_default_config_log_level()
116 self.set_default_config_log_level()
116 self.attempt(self.pre_load_command_line_config)
117 self.attempt(self.pre_load_command_line_config)
117 self.attempt(self.load_command_line_config, action='abort')
118 self.attempt(self.load_command_line_config, action='abort')
118 self.set_command_line_config_log_level()
119 self.set_command_line_config_log_level()
119 self.attempt(self.post_load_command_line_config)
120 self.attempt(self.post_load_command_line_config)
120 self.log_command_line_config()
121 self.log_command_line_config()
121 self.attempt(self.find_ipythondir)
122 self.attempt(self.find_ipythondir)
123 self.attempt(self.find_resources)
122 self.attempt(self.find_config_file_name)
124 self.attempt(self.find_config_file_name)
123 self.attempt(self.find_config_file_paths)
125 self.attempt(self.find_config_file_paths)
124 self.attempt(self.pre_load_file_config)
126 self.attempt(self.pre_load_file_config)
125 self.attempt(self.load_file_config)
127 self.attempt(self.load_file_config)
126 self.set_file_config_log_level()
128 self.set_file_config_log_level()
127 self.attempt(self.post_load_file_config)
129 self.attempt(self.post_load_file_config)
128 self.log_file_config()
130 self.log_file_config()
129 self.attempt(self.merge_configs)
131 self.attempt(self.merge_configs)
130 self.log_master_config()
132 self.log_master_config()
131 self.attempt(self.pre_construct)
133 self.attempt(self.pre_construct)
132 self.attempt(self.construct)
134 self.attempt(self.construct)
133 self.attempt(self.post_construct)
135 self.attempt(self.post_construct)
134 self.attempt(self.start_app)
136 self.attempt(self.start_app)
135
137
136 #-------------------------------------------------------------------------
138 #-------------------------------------------------------------------------
137 # Various stages of Application creation
139 # Various stages of Application creation
138 #-------------------------------------------------------------------------
140 #-------------------------------------------------------------------------
139
141
140 def create_default_config(self):
142 def create_default_config(self):
141 """Create defaults that can't be set elsewhere.
143 """Create defaults that can't be set elsewhere.
142
144
143 For the most part, we try to set default in the class attributes
145 For the most part, we try to set default in the class attributes
144 of Components. But, defaults the top-level Application (which is
146 of Components. But, defaults the top-level Application (which is
145 not a HasTraitlets or Component) are not set in this way. Instead
147 not a HasTraitlets or Component) are not set in this way. Instead
146 we set them here. The Global section is for variables like this that
148 we set them here. The Global section is for variables like this that
147 don't belong to a particular component.
149 don't belong to a particular component.
148 """
150 """
149 self.default_config = Config()
151 self.default_config = Config()
150 self.default_config.Global.ipythondir = get_ipython_dir()
152 self.default_config.Global.ipythondir = get_ipython_dir()
151 self.default_config.Global.log_level = self.log_level
153 self.default_config.Global.log_level = self.log_level
152
154
153 def log_default_config(self):
155 def log_default_config(self):
154 self.log.debug('Default config loaded:')
156 self.log.debug('Default config loaded:')
155 self.log.debug(repr(self.default_config))
157 self.log.debug(repr(self.default_config))
156
158
157 def set_default_config_log_level(self):
159 def set_default_config_log_level(self):
158 try:
160 try:
159 self.log_level = self.default_config.Global.log_level
161 self.log_level = self.default_config.Global.log_level
160 except AttributeError:
162 except AttributeError:
161 # Fallback to the default_log_level class attribute
163 # Fallback to the default_log_level class attribute
162 pass
164 pass
163
165
164 def create_command_line_config(self):
166 def create_command_line_config(self):
165 """Create and return a command line config loader."""
167 """Create and return a command line config loader."""
166 return BaseAppArgParseConfigLoader(
168 return BaseAppArgParseConfigLoader(
167 description=self.description,
169 description=self.description,
168 version=release.version
170 version=release.version
169 )
171 )
170
172
171 def pre_load_command_line_config(self):
173 def pre_load_command_line_config(self):
172 """Do actions just before loading the command line config."""
174 """Do actions just before loading the command line config."""
173 pass
175 pass
174
176
175 def load_command_line_config(self):
177 def load_command_line_config(self):
176 """Load the command line config."""
178 """Load the command line config."""
177 loader = self.create_command_line_config()
179 loader = self.create_command_line_config()
178 self.command_line_config = loader.load_config()
180 self.command_line_config = loader.load_config()
179 self.extra_args = loader.get_extra_args()
181 self.extra_args = loader.get_extra_args()
180
182
181 def set_command_line_config_log_level(self):
183 def set_command_line_config_log_level(self):
182 try:
184 try:
183 self.log_level = self.command_line_config.Global.log_level
185 self.log_level = self.command_line_config.Global.log_level
184 except AttributeError:
186 except AttributeError:
185 pass
187 pass
186
188
187 def post_load_command_line_config(self):
189 def post_load_command_line_config(self):
188 """Do actions just after loading the command line config."""
190 """Do actions just after loading the command line config."""
189 pass
191 pass
190
192
191 def log_command_line_config(self):
193 def log_command_line_config(self):
192 self.log.debug("Command line config loaded:")
194 self.log.debug("Command line config loaded:")
193 self.log.debug(repr(self.command_line_config))
195 self.log.debug(repr(self.command_line_config))
194
196
195 def find_ipythondir(self):
197 def find_ipythondir(self):
196 """Set the IPython directory.
198 """Set the IPython directory.
197
199
198 This sets ``self.ipythondir``, but the actual value that is passed
200 This sets ``self.ipythondir``, but the actual value that is passed
199 to the application is kept in either ``self.default_config`` or
201 to the application is kept in either ``self.default_config`` or
200 ``self.command_line_config``. This also adds ``self.ipythondir`` to
202 ``self.command_line_config``. This also adds ``self.ipythondir`` to
201 ``sys.path`` so config files there can be references by other config
203 ``sys.path`` so config files there can be references by other config
202 files.
204 files.
203 """
205 """
204
206
205 try:
207 try:
206 self.ipythondir = self.command_line_config.Global.ipythondir
208 self.ipythondir = self.command_line_config.Global.ipythondir
207 except AttributeError:
209 except AttributeError:
208 self.ipythondir = self.default_config.Global.ipythondir
210 self.ipythondir = self.default_config.Global.ipythondir
209 sys.path.append(os.path.abspath(self.ipythondir))
211 sys.path.append(os.path.abspath(self.ipythondir))
210 if not os.path.isdir(self.ipythondir):
212 if not os.path.isdir(self.ipythondir):
211 os.makedirs(self.ipythondir, mode=0777)
213 os.makedirs(self.ipythondir, mode=0777)
212 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
214 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
213
215
216 def find_resources(self):
217 """Find other resources that need to be in place.
218
219 Things like cluster directories need to be in place to find the
220 config file. These happen right after the IPython directory has
221 been set.
222 """
223 pass
224
214 def find_config_file_name(self):
225 def find_config_file_name(self):
215 """Find the config file name for this application.
226 """Find the config file name for this application.
216
227
217 This must set ``self.config_file_name`` to the filename of the
228 This must set ``self.config_file_name`` to the filename of the
218 config file to use (just the filename). The search paths for the
229 config file to use (just the filename). The search paths for the
219 config file are set in :meth:`find_config_file_paths` and then passed
230 config file are set in :meth:`find_config_file_paths` and then passed
220 to the config file loader where they are resolved to an absolute path.
231 to the config file loader where they are resolved to an absolute path.
221
232
222 If a profile has been set at the command line, this will resolve
233 If a profile has been set at the command line, this will resolve
223 it.
234 it.
224 """
235 """
225
236
226 try:
237 try:
227 self.config_file_name = self.command_line_config.Global.config_file
238 self.config_file_name = self.command_line_config.Global.config_file
228 except AttributeError:
239 except AttributeError:
229 pass
240 pass
230
241
231 try:
242 try:
232 self.profile_name = self.command_line_config.Global.profile
243 self.profile_name = self.command_line_config.Global.profile
233 name_parts = self.config_file_name.split('.')
244 name_parts = self.config_file_name.split('.')
234 name_parts.insert(1, '_' + self.profile_name + '.')
245 name_parts.insert(1, '_' + self.profile_name + '.')
235 self.config_file_name = ''.join(name_parts)
246 self.config_file_name = ''.join(name_parts)
236 except AttributeError:
247 except AttributeError:
237 pass
248 pass
238
249
239 def find_config_file_paths(self):
250 def find_config_file_paths(self):
240 """Set the search paths for resolving the config file.
251 """Set the search paths for resolving the config file.
241
252
242 This must set ``self.config_file_paths`` to a sequence of search
253 This must set ``self.config_file_paths`` to a sequence of search
243 paths to pass to the config file loader.
254 paths to pass to the config file loader.
244 """
255 """
245 self.config_file_paths = (os.getcwd(), self.ipythondir)
256 self.config_file_paths = (os.getcwd(), self.ipythondir)
246
257
247 def pre_load_file_config(self):
258 def pre_load_file_config(self):
248 """Do actions before the config file is loaded."""
259 """Do actions before the config file is loaded."""
249 pass
260 pass
250
261
251 def load_file_config(self):
262 def load_file_config(self):
252 """Load the config file.
263 """Load the config file.
253
264
254 This tries to load the config file from disk. If successful, the
265 This tries to load the config file from disk. If successful, the
255 ``CONFIG_FILE`` config variable is set to the resolved config file
266 ``CONFIG_FILE`` config variable is set to the resolved config file
256 location. If not successful, an empty config is used.
267 location. If not successful, an empty config is used.
257 """
268 """
258 self.log.debug("Attempting to load config file: %s" % self.config_file_name)
269 self.log.debug("Attempting to load config file: %s" % self.config_file_name)
259 loader = PyFileConfigLoader(self.config_file_name,
270 loader = PyFileConfigLoader(self.config_file_name,
260 path=self.config_file_paths)
271 path=self.config_file_paths)
261 try:
272 try:
262 self.file_config = loader.load_config()
273 self.file_config = loader.load_config()
263 self.file_config.Global.config_file = loader.full_filename
274 self.file_config.Global.config_file = loader.full_filename
264 except IOError:
275 except IOError:
265 # Only warn if the default config file was NOT being used.
276 # Only warn if the default config file was NOT being used.
266 if not self.config_file_name==self.default_config_file_name:
277 if not self.config_file_name==self.default_config_file_name:
267 self.log.warn("Config file not found, skipping: %s" % \
278 self.log.warn("Config file not found, skipping: %s" % \
268 self.config_file_name, exc_info=True)
279 self.config_file_name, exc_info=True)
269 self.file_config = Config()
280 self.file_config = Config()
270 except:
281 except:
271 self.log.warn("Error loading config file: %s" % \
282 self.log.warn("Error loading config file: %s" % \
272 self.config_file_name, exc_info=True)
283 self.config_file_name, exc_info=True)
273 self.file_config = Config()
284 self.file_config = Config()
274
285
275 def set_file_config_log_level(self):
286 def set_file_config_log_level(self):
276 # We need to keeep self.log_level updated. But we only use the value
287 # We need to keeep self.log_level updated. But we only use the value
277 # of the file_config if a value was not specified at the command
288 # of the file_config if a value was not specified at the command
278 # line, because the command line overrides everything.
289 # line, because the command line overrides everything.
279 if not hasattr(self.command_line_config.Global, 'log_level'):
290 if not hasattr(self.command_line_config.Global, 'log_level'):
280 try:
291 try:
281 self.log_level = self.file_config.Global.log_level
292 self.log_level = self.file_config.Global.log_level
282 except AttributeError:
293 except AttributeError:
283 pass # Use existing value
294 pass # Use existing value
284
295
285 def post_load_file_config(self):
296 def post_load_file_config(self):
286 """Do actions after the config file is loaded."""
297 """Do actions after the config file is loaded."""
287 pass
298 pass
288
299
289 def log_file_config(self):
300 def log_file_config(self):
290 if hasattr(self.file_config.Global, 'config_file'):
301 if hasattr(self.file_config.Global, 'config_file'):
291 self.log.debug("Config file loaded: %s" % self.file_config.Global.config_file)
302 self.log.debug("Config file loaded: %s" % self.file_config.Global.config_file)
292 self.log.debug(repr(self.file_config))
303 self.log.debug(repr(self.file_config))
293
304
294 def merge_configs(self):
305 def merge_configs(self):
295 """Merge the default, command line and file config objects."""
306 """Merge the default, command line and file config objects."""
296 config = Config()
307 config = Config()
297 config._merge(self.default_config)
308 config._merge(self.default_config)
298 config._merge(self.file_config)
309 config._merge(self.file_config)
299 config._merge(self.command_line_config)
310 config._merge(self.command_line_config)
300 self.master_config = config
311 self.master_config = config
301
312
302 def log_master_config(self):
313 def log_master_config(self):
303 self.log.debug("Master config created:")
314 self.log.debug("Master config created:")
304 self.log.debug(repr(self.master_config))
315 self.log.debug(repr(self.master_config))
305
316
306 def pre_construct(self):
317 def pre_construct(self):
307 """Do actions after the config has been built, but before construct."""
318 """Do actions after the config has been built, but before construct."""
308 pass
319 pass
309
320
310 def construct(self):
321 def construct(self):
311 """Construct the main components that make up this app."""
322 """Construct the main components that make up this app."""
312 self.log.debug("Constructing components for application")
323 self.log.debug("Constructing components for application")
313
324
314 def post_construct(self):
325 def post_construct(self):
315 """Do actions after construct, but before starting the app."""
326 """Do actions after construct, but before starting the app."""
316 pass
327 pass
317
328
318 def start_app(self):
329 def start_app(self):
319 """Actually start the app."""
330 """Actually start the app."""
320 self.log.debug("Starting application")
331 self.log.debug("Starting application")
321
332
322 #-------------------------------------------------------------------------
333 #-------------------------------------------------------------------------
323 # Utility methods
334 # Utility methods
324 #-------------------------------------------------------------------------
335 #-------------------------------------------------------------------------
325
336
326 def abort(self):
337 def abort(self):
327 """Abort the starting of the application."""
338 """Abort the starting of the application."""
328 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
339 if self._exiting:
329 sys.exit(1)
340 pass
341 else:
342 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
343 self._exiting = True
344 sys.exit(1)
330
345
331 def exit(self):
346 def exit(self):
332 self.log.critical("Aborting application: %s" % self.name)
347 if self._exiting:
333 sys.exit(1)
348 pass
349 else:
350 self.log.debug("Exiting application: %s" % self.name)
351 self._exiting = True
352 sys.exit(1)
334
353
335 def attempt(self, func, action='abort'):
354 def attempt(self, func, action='abort'):
336 try:
355 try:
337 func()
356 func()
338 except SystemExit:
357 except SystemExit:
339 self.exit()
358 raise
340 except:
359 except:
341 if action == 'abort':
360 if action == 'abort':
342 self.abort()
361 self.abort()
343 elif action == 'exit':
362 elif action == 'exit':
344 self.exit()
363 self.exit()
345
364
@@ -1,263 +1,352 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 import os
18 import os
19 import shutil
19 import shutil
20
20
21 from IPython.core import release
21 from IPython.core import release
22 from IPython.config.loader import PyFileConfigLoader
22 from IPython.config.loader import PyFileConfigLoader
23 from IPython.core.application import Application
23 from IPython.core.application import Application
24 from IPython.core.component import Component
24 from IPython.core.component import Component
25 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
25 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
26 from IPython.utils.traitlets import Unicode
26 from IPython.utils.traitlets import Unicode, Bool
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Imports
29 # Imports
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32
32
33 class ClusterDirError(Exception):
34 pass
35
36
33 class ClusterDir(Component):
37 class ClusterDir(Component):
34 """An object to manage the cluster directory and its resources.
38 """An object to manage the cluster directory and its resources.
35
39
36 The cluster directory is used by :command:`ipcontroller`,
40 The cluster directory is used by :command:`ipcontroller`,
37 :command:`ipcontroller` and :command:`ipcontroller` to manage the
41 :command:`ipcontroller` and :command:`ipcontroller` to manage the
38 configuration, logging and security of these applications.
42 configuration, logging and security of these applications.
39
43
40 This object knows how to find, create and manage these directories. This
44 This object knows how to find, create and manage these directories. This
41 should be used by any code that want's to handle cluster directories.
45 should be used by any code that want's to handle cluster directories.
42 """
46 """
43
47
44 security_dir_name = Unicode('security')
48 security_dir_name = Unicode('security')
45 log_dir_name = Unicode('log')
49 log_dir_name = Unicode('log')
46 security_dir = Unicode()
50 security_dir = Unicode()
47 log_dir = Unicode('')
51 log_dir = Unicode('')
48 location = Unicode('')
52 location = Unicode('')
49
53
50 def __init__(self, location):
54 def __init__(self, location):
51 super(ClusterDir, self).__init__(None)
55 super(ClusterDir, self).__init__(None)
52 self.location = location
56 self.location = location
53
57
54 def _location_changed(self, name, old, new):
58 def _location_changed(self, name, old, new):
55 if not os.path.isdir(new):
59 if not os.path.isdir(new):
56 os.makedirs(new, mode=0777)
60 os.makedirs(new, mode=0777)
57 else:
61 else:
58 os.chmod(new, 0777)
62 os.chmod(new, 0777)
59 self.security_dir = os.path.join(new, self.security_dir_name)
63 self.security_dir = os.path.join(new, self.security_dir_name)
60 self.log_dir = os.path.join(new, self.log_dir_name)
64 self.log_dir = os.path.join(new, self.log_dir_name)
61 self.check_dirs()
65 self.check_dirs()
62
66
63 def _log_dir_changed(self, name, old, new):
67 def _log_dir_changed(self, name, old, new):
64 self.check_log_dir()
68 self.check_log_dir()
65
69
66 def check_log_dir(self):
70 def check_log_dir(self):
67 if not os.path.isdir(self.log_dir):
71 if not os.path.isdir(self.log_dir):
68 os.mkdir(self.log_dir, 0777)
72 os.mkdir(self.log_dir, 0777)
69 else:
73 else:
70 os.chmod(self.log_dir, 0777)
74 os.chmod(self.log_dir, 0777)
71
75
72 def _security_dir_changed(self, name, old, new):
76 def _security_dir_changed(self, name, old, new):
73 self.check_security_dir()
77 self.check_security_dir()
74
78
75 def check_security_dir(self):
79 def check_security_dir(self):
76 if not os.path.isdir(self.security_dir):
80 if not os.path.isdir(self.security_dir):
77 os.mkdir(self.security_dir, 0700)
81 os.mkdir(self.security_dir, 0700)
78 else:
82 else:
79 os.chmod(self.security_dir, 0700)
83 os.chmod(self.security_dir, 0700)
80
84
81 def check_dirs(self):
85 def check_dirs(self):
82 self.check_security_dir()
86 self.check_security_dir()
83 self.check_log_dir()
87 self.check_log_dir()
84
88
85 def load_config_file(self, filename):
89 def load_config_file(self, filename):
86 """Load a config file from the top level of the cluster dir.
90 """Load a config file from the top level of the cluster dir.
87
91
88 Parameters
92 Parameters
89 ----------
93 ----------
90 filename : unicode or str
94 filename : unicode or str
91 The filename only of the config file that must be located in
95 The filename only of the config file that must be located in
92 the top-level of the cluster directory.
96 the top-level of the cluster directory.
93 """
97 """
94 loader = PyFileConfigLoader(filename, self.location)
98 loader = PyFileConfigLoader(filename, self.location)
95 return loader.load_config()
99 return loader.load_config()
96
100
97 def copy_config_file(self, config_file, path=None, overwrite=False):
101 def copy_config_file(self, config_file, path=None, overwrite=False):
98 """Copy a default config file into the active cluster directory.
102 """Copy a default config file into the active cluster directory.
99
103
100 Default configuration files are kept in :mod:`IPython.config.default`.
104 Default configuration files are kept in :mod:`IPython.config.default`.
101 This function moves these from that location to the working cluster
105 This function moves these from that location to the working cluster
102 directory.
106 directory.
103 """
107 """
104 if path is None:
108 if path is None:
105 import IPython.config.default
109 import IPython.config.default
106 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
110 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
107 path = os.path.sep.join(path)
111 path = os.path.sep.join(path)
108 src = os.path.join(path, config_file)
112 src = os.path.join(path, config_file)
109 dst = os.path.join(self.location, config_file)
113 dst = os.path.join(self.location, config_file)
110 if not os.path.isfile(dst) or overwrite:
114 if not os.path.isfile(dst) or overwrite:
111 shutil.copy(src, dst)
115 shutil.copy(src, dst)
112
116
113 def copy_all_config_files(self, path=None, overwrite=False):
117 def copy_all_config_files(self, path=None, overwrite=False):
114 """Copy all config files into the active cluster directory."""
118 """Copy all config files into the active cluster directory."""
115 for f in ['ipcontroller_config.py', 'ipengine_config.py',
119 for f in ['ipcontroller_config.py', 'ipengine_config.py',
116 'ipcluster_config.py']:
120 'ipcluster_config.py']:
117 self.copy_config_file(f, path=path, overwrite=overwrite)
121 self.copy_config_file(f, path=path, overwrite=overwrite)
118
122
119 @classmethod
123 @classmethod
120 def find_cluster_dir_by_profile(cls, path, profile='default'):
124 def create_cluster_dir(csl, cluster_dir):
121 """Find/create a cluster dir by profile name and return its ClusterDir.
125 """Create a new cluster directory given a full path.
122
126
123 This will create the cluster directory if it doesn't exist.
127 Parameters
128 ----------
129 cluster_dir : str
130 The full path to the cluster directory. If it does exist, it will
131 be used. If not, it will be created.
132 """
133 return ClusterDir(cluster_dir)
134
135 @classmethod
136 def create_cluster_dir_by_profile(cls, path, profile='default'):
137 """Create a cluster dir by profile name and path.
138
139 Parameters
140 ----------
141 path : str
142 The path (directory) to put the cluster directory in.
143 profile : str
144 The name of the profile. The name of the cluster directory will
145 be "cluster_<profile>".
146 """
147 if not os.path.isdir(path):
148 raise ClusterDirError('Directory not found: %s' % path)
149 cluster_dir = os.path.join(path, 'cluster_' + profile)
150 return ClusterDir(cluster_dir)
151
152 @classmethod
153 def find_cluster_dir_by_profile(cls, ipythondir, profile='default'):
154 """Find an existing cluster dir by profile name, return its ClusterDir.
155
156 This searches through a sequence of paths for a cluster dir. If it
157 is not found, a :class:`ClusterDirError` exception will be raised.
158
159 The search path algorithm is:
160 1. ``os.getcwd()``
161 2. ``ipythondir``
162 3. The directories found in the ":" separated
163 :env:`IPCLUSTERDIR_PATH` environment variable.
124
164
125 Parameters
165 Parameters
126 ----------
166 ----------
127 path : unicode or str
167 ipythondir : unicode or str
128 The directory path to look for the cluster directory in.
168 The IPython directory to use.
129 profile : unicode or str
169 profile : unicode or str
130 The name of the profile. The name of the cluster directory
170 The name of the profile. The name of the cluster directory
131 will be "cluster_<profile>".
171 will be "cluster_<profile>".
132 """
172 """
133 dirname = 'cluster_' + profile
173 dirname = 'cluster_' + profile
134 cluster_dir = os.path.join(os.getcwd(), dirname)
174 cluster_dir_paths = os.environ.get('IPCLUSTERDIR_PATH','')
135 if os.path.isdir(cluster_dir):
175 if cluster_dir_paths:
136 return ClusterDir(cluster_dir)
176 cluster_dir_paths = cluster_dir_paths.split(':')
137 else:
177 else:
138 if not os.path.isdir(path):
178 cluster_dir_paths = []
139 raise IOError("Directory doesn't exist: %s" % path)
179 paths = [os.getcwd(), ipythondir] + cluster_dir_paths
140 cluster_dir = os.path.join(path, dirname)
180 for p in paths:
141 return ClusterDir(cluster_dir)
181 cluster_dir = os.path.join(p, dirname)
182 if os.path.isdir(cluster_dir):
183 return ClusterDir(cluster_dir)
184 else:
185 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
142
186
143 @classmethod
187 @classmethod
144 def find_cluster_dir(cls, cluster_dir):
188 def find_cluster_dir(cls, cluster_dir):
145 """Find/create a cluster dir and return its ClusterDir.
189 """Find/create a cluster dir and return its ClusterDir.
146
190
147 This will create the cluster directory if it doesn't exist.
191 This will create the cluster directory if it doesn't exist.
148
192
149 Parameters
193 Parameters
150 ----------
194 ----------
151 cluster_dir : unicode or str
195 cluster_dir : unicode or str
152 The path of the cluster directory. This is expanded using
196 The path of the cluster directory. This is expanded using
153 :func:`os.path.expandvars` and :func:`os.path.expanduser`.
197 :func:`os.path.expandvars` and :func:`os.path.expanduser`.
154 """
198 """
155 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
199 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
200 if not os.path.isdir(cluster_dir):
201 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
156 return ClusterDir(cluster_dir)
202 return ClusterDir(cluster_dir)
157
203
158
204
159 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
205 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
160 """Default command line options for IPython cluster applications."""
206 """Default command line options for IPython cluster applications."""
161
207
162 def _add_other_arguments(self):
208 def _add_other_arguments(self):
163 self.parser.add_argument('-ipythondir', '--ipython-dir',
209 self.parser.add_argument('-ipythondir', '--ipython-dir',
164 dest='Global.ipythondir',type=str,
210 dest='Global.ipythondir',type=str,
165 help='Set to override default location of Global.ipythondir.',
211 help='Set to override default location of Global.ipythondir.',
166 default=NoConfigDefault,
212 default=NoConfigDefault,
167 metavar='Global.ipythondir')
213 metavar='Global.ipythondir')
168 self.parser.add_argument('-p','-profile', '--profile',
214 self.parser.add_argument('-p','-profile', '--profile',
169 dest='Global.profile',type=str,
215 dest='Global.profile',type=str,
170 help='The string name of the profile to be used. This determines '
216 help='The string name of the profile to be used. This determines '
171 'the name of the cluster dir as: cluster_<profile>. The default profile '
217 'the name of the cluster dir as: cluster_<profile>. The default profile '
172 'is named "default". The cluster directory is resolve this way '
218 'is named "default". The cluster directory is resolve this way '
173 'if the --cluster-dir option is not used.',
219 'if the --cluster-dir option is not used.',
174 default=NoConfigDefault,
220 default=NoConfigDefault,
175 metavar='Global.profile')
221 metavar='Global.profile')
176 self.parser.add_argument('-log_level', '--log-level',
222 self.parser.add_argument('-log_level', '--log-level',
177 dest="Global.log_level",type=int,
223 dest="Global.log_level",type=int,
178 help='Set the log level (0,10,20,30,40,50). Default is 30.',
224 help='Set the log level (0,10,20,30,40,50). Default is 30.',
179 default=NoConfigDefault)
225 default=NoConfigDefault,
226 metavar="Global.log_level")
180 self.parser.add_argument('-cluster_dir', '--cluster-dir',
227 self.parser.add_argument('-cluster_dir', '--cluster-dir',
181 dest='Global.cluster_dir',type=str,
228 dest='Global.cluster_dir',type=str,
182 help='Set the cluster dir. This overrides the logic used by the '
229 help='Set the cluster dir. This overrides the logic used by the '
183 '--profile option.',
230 '--profile option.',
184 default=NoConfigDefault,
231 default=NoConfigDefault,
185 metavar='Global.cluster_dir')
232 metavar='Global.cluster_dir')
186
233
187
234
188 class ApplicationWithClusterDir(Application):
235 class ApplicationWithClusterDir(Application):
189 """An application that puts everything into a cluster directory.
236 """An application that puts everything into a cluster directory.
190
237
191 Instead of looking for things in the ipythondir, this type of application
238 Instead of looking for things in the ipythondir, this type of application
192 will use its own private directory called the "cluster directory"
239 will use its own private directory called the "cluster directory"
193 for things like config files, log files, etc.
240 for things like config files, log files, etc.
194
241
195 The cluster directory is resolved as follows:
242 The cluster directory is resolved as follows:
196
243
197 * If the ``--cluster-dir`` option is given, it is used.
244 * If the ``--cluster-dir`` option is given, it is used.
198 * If ``--cluster-dir`` is not given, the application directory is
245 * If ``--cluster-dir`` is not given, the application directory is
199 resolve using the profile name as ``cluster_<profile>``. The search
246 resolve using the profile name as ``cluster_<profile>``. The search
200 path for this directory is then i) cwd if it is found there
247 path for this directory is then i) cwd if it is found there
201 and ii) in ipythondir otherwise.
248 and ii) in ipythondir otherwise.
202
249
203 The config file for the application is to be put in the cluster
250 The config file for the application is to be put in the cluster
204 dir and named the value of the ``config_file_name`` class attribute.
251 dir and named the value of the ``config_file_name`` class attribute.
205 """
252 """
206
253
254 auto_create_cluster_dir = True
255
207 def create_default_config(self):
256 def create_default_config(self):
208 super(ApplicationWithClusterDir, self).create_default_config()
257 super(ApplicationWithClusterDir, self).create_default_config()
209 self.default_config.Global.profile = 'default'
258 self.default_config.Global.profile = 'default'
210 self.default_config.Global.cluster_dir = ''
259 self.default_config.Global.cluster_dir = ''
211
260
212 def create_command_line_config(self):
261 def create_command_line_config(self):
213 """Create and return a command line config loader."""
262 """Create and return a command line config loader."""
214 return AppWithClusterDirArgParseConfigLoader(
263 return AppWithClusterDirArgParseConfigLoader(
215 description=self.description,
264 description=self.description,
216 version=release.version
265 version=release.version
217 )
266 )
218
267
219 def find_config_file_name(self):
268 def find_resources(self):
220 """Find the config file name for this application."""
269 """This resolves the cluster directory.
221 # For this type of Application it should be set as a class attribute.
222 if not hasattr(self, 'config_file_name'):
223 self.log.critical("No config filename found")
224
225 def find_config_file_paths(self):
226 """This resolves the cluster directory and sets ``config_file_paths``.
227
270
228 This does the following:
271 This tries to find the cluster directory and if successful, it will
229 * Create the :class:`ClusterDir` object for the application.
272 have done:
230 * Set the ``cluster_dir`` attribute of the application and config
273 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
274 the application.
275 * Sets ``self.cluster_dir`` attribute of the application and config
231 objects.
276 objects.
232 * Set ``config_file_paths`` to point to the cluster directory.
277
278 The algorithm used for this is as follows:
279 1. Try ``Global.cluster_dir``.
280 2. Try using ``Global.profile``.
281 3. If both of these fail and ``self.auto_create_cluster_dir`` is
282 ``True``, then create the new cluster dir in the IPython directory.
283 4. If all fails, then raise :class:`ClusterDirError`.
233 """
284 """
234
285
235 # Create the ClusterDir object for managing everything
236 try:
286 try:
237 cluster_dir = self.command_line_config.Global.cluster_dir
287 cluster_dir = self.command_line_config.Global.cluster_dir
238 except AttributeError:
288 except AttributeError:
239 cluster_dir = self.default_config.Global.cluster_dir
289 cluster_dir = self.default_config.Global.cluster_dir
240 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
290 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
241 if cluster_dir:
291 try:
242 # Just use cluster_dir
243 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
292 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
293 except ClusterDirError:
294 pass
244 else:
295 else:
245 # Then look for a profile
296 self.log.info('Using existing cluster dir: %s' % \
246 try:
297 self.cluster_dir_obj.location
247 self.profile = self.command_line_config.Global.profile
298 )
248 except AttributeError:
299 self.finish_cluster_dir()
249 self.profile = self.default_config.Global.profile
300 return
301
302 try:
303 self.profile = self.command_line_config.Global.profile
304 except AttributeError:
305 self.profile = self.default_config.Global.profile
306 try:
250 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
307 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
251 self.ipythondir, self.profile)
308 self.ipythondir, self.profile)
309 except ClusterDirError:
310 pass
311 else:
312 self.log.info('Using existing cluster dir: %s' % \
313 self.cluster_dir_obj.location
314 )
315 self.finish_cluster_dir()
316 return
317
318 if self.auto_create_cluster_dir:
319 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
320 self.ipythondir, self.profile
321 )
322 self.log.info('Creating new cluster dir: %s' % \
323 self.cluster_dir_obj.location
324 )
325 self.finish_cluster_dir()
326 else:
327 raise ClusterDirError('Could not find a valid cluster directory.')
252
328
329 def finish_cluster_dir(self):
253 # Set the cluster directory
330 # Set the cluster directory
254 self.cluster_dir = self.cluster_dir_obj.location
331 self.cluster_dir = self.cluster_dir_obj.location
255
332
256 # These have to be set because they could be different from the one
333 # These have to be set because they could be different from the one
257 # that we just computed. Because command line has the highest
334 # that we just computed. Because command line has the highest
258 # priority, this will always end up in the master_config.
335 # priority, this will always end up in the master_config.
259 self.default_config.Global.cluster_dir = self.cluster_dir
336 self.default_config.Global.cluster_dir = self.cluster_dir
260 self.command_line_config.Global.cluster_dir = self.cluster_dir
337 self.command_line_config.Global.cluster_dir = self.cluster_dir
261
338
262 # Set the search path to the cluster directory
339 # Set the search path to the cluster directory
263 self.config_file_paths = (self.cluster_dir,)
340 self.config_file_paths = (self.cluster_dir,)
341
342 def find_config_file_name(self):
343 """Find the config file name for this application."""
344 # For this type of Application it should be set as a class attribute.
345 if not hasattr(self, 'config_file_name'):
346 self.log.critical("No config filename found")
347
348 def find_config_file_paths(self):
349 # Set the search path to the cluster directory
350 self.config_file_paths = (self.cluster_dir,)
351
352
@@ -1,274 +1,275 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 import copy
18 import copy
19 import os
19 import os
20 import sys
20 import sys
21
21
22 from twisted.application import service
22 from twisted.application import service
23 from twisted.internet import reactor
23 from twisted.internet import reactor
24 from twisted.python import log
24 from twisted.python import log
25
25
26 from IPython.config.loader import Config, NoConfigDefault
26 from IPython.config.loader import Config, NoConfigDefault
27
27
28 from IPython.kernel.clusterdir import (
28 from IPython.kernel.clusterdir import (
29 ApplicationWithClusterDir,
29 ApplicationWithClusterDir,
30 AppWithClusterDirArgParseConfigLoader
30 AppWithClusterDirArgParseConfigLoader
31 )
31 )
32
32
33 from IPython.core import release
33 from IPython.core import release
34
34
35 from IPython.utils.traitlets import Str, Instance
35 from IPython.utils.traitlets import Str, Instance
36
36
37 from IPython.kernel import controllerservice
37 from IPython.kernel import controllerservice
38
38
39 from IPython.kernel.fcutil import FCServiceFactory
39 from IPython.kernel.fcutil import FCServiceFactory
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Default interfaces
42 # Default interfaces
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45
45
46 # The default client interfaces for FCClientServiceFactory.interfaces
46 # The default client interfaces for FCClientServiceFactory.interfaces
47 default_client_interfaces = Config()
47 default_client_interfaces = Config()
48 default_client_interfaces.Task.interface_chain = [
48 default_client_interfaces.Task.interface_chain = [
49 'IPython.kernel.task.ITaskController',
49 'IPython.kernel.task.ITaskController',
50 'IPython.kernel.taskfc.IFCTaskController'
50 'IPython.kernel.taskfc.IFCTaskController'
51 ]
51 ]
52
52
53 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
53 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
54
54
55 default_client_interfaces.MultiEngine.interface_chain = [
55 default_client_interfaces.MultiEngine.interface_chain = [
56 'IPython.kernel.multiengine.IMultiEngine',
56 'IPython.kernel.multiengine.IMultiEngine',
57 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
57 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
58 ]
58 ]
59
59
60 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
60 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
61
61
62 # Make this a dict we can pass to Config.__init__ for the default
62 # Make this a dict we can pass to Config.__init__ for the default
63 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
63 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
64
64
65
65
66
66
67 # The default engine interfaces for FCEngineServiceFactory.interfaces
67 # The default engine interfaces for FCEngineServiceFactory.interfaces
68 default_engine_interfaces = Config()
68 default_engine_interfaces = Config()
69 default_engine_interfaces.Default.interface_chain = [
69 default_engine_interfaces.Default.interface_chain = [
70 'IPython.kernel.enginefc.IFCControllerBase'
70 'IPython.kernel.enginefc.IFCControllerBase'
71 ]
71 ]
72
72
73 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
73 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
74
74
75 # Make this a dict we can pass to Config.__init__ for the default
75 # Make this a dict we can pass to Config.__init__ for the default
76 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
76 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
77
77
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Service factories
80 # Service factories
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82
82
83
83
84 class FCClientServiceFactory(FCServiceFactory):
84 class FCClientServiceFactory(FCServiceFactory):
85 """A Foolscap implementation of the client services."""
85 """A Foolscap implementation of the client services."""
86
86
87 cert_file = Str('ipcontroller-client.pem', config=True)
87 cert_file = Str('ipcontroller-client.pem', config=True)
88 interfaces = Instance(klass=Config, kw=default_client_interfaces,
88 interfaces = Instance(klass=Config, kw=default_client_interfaces,
89 allow_none=False, config=True)
89 allow_none=False, config=True)
90
90
91
91
92 class FCEngineServiceFactory(FCServiceFactory):
92 class FCEngineServiceFactory(FCServiceFactory):
93 """A Foolscap implementation of the engine services."""
93 """A Foolscap implementation of the engine services."""
94
94
95 cert_file = Str('ipcontroller-engine.pem', config=True)
95 cert_file = Str('ipcontroller-engine.pem', config=True)
96 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
96 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
97 allow_none=False, config=True)
97 allow_none=False, config=True)
98
98
99
99
100 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
101 # The main application
101 # The main application
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103
103
104
104
105 cl_args = (
105 cl_args = (
106 # Client config
106 # Client config
107 (('--client-ip',), dict(
107 (('--client-ip',), dict(
108 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
108 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
109 help='The IP address or hostname the controller will listen on for '
109 help='The IP address or hostname the controller will listen on for '
110 'client connections.',
110 'client connections.',
111 metavar='FCClientServiceFactory.ip')
111 metavar='FCClientServiceFactory.ip')
112 ),
112 ),
113 (('--client-port',), dict(
113 (('--client-port',), dict(
114 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
114 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
115 help='The port the controller will listen on for client connections. '
115 help='The port the controller will listen on for client connections. '
116 'The default is to use 0, which will autoselect an open port.',
116 'The default is to use 0, which will autoselect an open port.',
117 metavar='FCClientServiceFactory.port')
117 metavar='FCClientServiceFactory.port')
118 ),
118 ),
119 (('--client-location',), dict(
119 (('--client-location',), dict(
120 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
120 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
121 help='The hostname or IP that clients should connect to. This does '
121 help='The hostname or IP that clients should connect to. This does '
122 'not control which interface the controller listens on. Instead, this '
122 'not control which interface the controller listens on. Instead, this '
123 'determines the hostname/IP that is listed in the FURL, which is how '
123 'determines the hostname/IP that is listed in the FURL, which is how '
124 'clients know where to connect. Useful if the controller is listening '
124 'clients know where to connect. Useful if the controller is listening '
125 'on multiple interfaces.',
125 'on multiple interfaces.',
126 metavar='FCClientServiceFactory.location')
126 metavar='FCClientServiceFactory.location')
127 ),
127 ),
128 # Engine config
128 # Engine config
129 (('--engine-ip',), dict(
129 (('--engine-ip',), dict(
130 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
130 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
131 help='The IP address or hostname the controller will listen on for '
131 help='The IP address or hostname the controller will listen on for '
132 'engine connections.',
132 'engine connections.',
133 metavar='FCEngineServiceFactory.ip')
133 metavar='FCEngineServiceFactory.ip')
134 ),
134 ),
135 (('--engine-port',), dict(
135 (('--engine-port',), dict(
136 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
136 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
137 help='The port the controller will listen on for engine connections. '
137 help='The port the controller will listen on for engine connections. '
138 'The default is to use 0, which will autoselect an open port.',
138 'The default is to use 0, which will autoselect an open port.',
139 metavar='FCEngineServiceFactory.port')
139 metavar='FCEngineServiceFactory.port')
140 ),
140 ),
141 (('--engine-location',), dict(
141 (('--engine-location',), dict(
142 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
142 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
143 help='The hostname or IP that engines should connect to. This does '
143 help='The hostname or IP that engines should connect to. This does '
144 'not control which interface the controller listens on. Instead, this '
144 'not control which interface the controller listens on. Instead, this '
145 'determines the hostname/IP that is listed in the FURL, which is how '
145 'determines the hostname/IP that is listed in the FURL, which is how '
146 'engines know where to connect. Useful if the controller is listening '
146 'engines know where to connect. Useful if the controller is listening '
147 'on multiple interfaces.',
147 'on multiple interfaces.',
148 metavar='FCEngineServiceFactory.location')
148 metavar='FCEngineServiceFactory.location')
149 ),
149 ),
150 # Global config
150 # Global config
151 (('--log-to-file',), dict(
151 (('--log-to-file',), dict(
152 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
152 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
153 help='Log to a file in the log directory (default is stdout)')
153 help='Log to a file in the log directory (default is stdout)')
154 ),
154 ),
155 (('-r','--reuse-furls'), dict(
155 (('-r','--reuse-furls'), dict(
156 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
156 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
157 help='Try to reuse all FURL files. If this is not set all FURL files '
157 help='Try to reuse all FURL files. If this is not set all FURL files '
158 'are deleted before the controller starts. This must be set if '
158 'are deleted before the controller starts. This must be set if '
159 'specific ports are specified by --engine-port or --client-port.')
159 'specific ports are specified by --engine-port or --client-port.')
160 ),
160 ),
161 (('-ns','--no-security'), dict(
161 (('-ns','--no-security'), dict(
162 action='store_false', dest='Global.secure', default=NoConfigDefault,
162 action='store_false', dest='Global.secure', default=NoConfigDefault,
163 help='Turn off SSL encryption for all connections.')
163 help='Turn off SSL encryption for all connections.')
164 )
164 )
165 )
165 )
166
166
167
167
168 class IPControllerAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
168 class IPControllerAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
169
169
170 arguments = cl_args
170 arguments = cl_args
171
171
172
172
173 default_config_file_name = 'ipcontroller_config.py'
173 default_config_file_name = 'ipcontroller_config.py'
174
174
175
175
176 class IPControllerApp(ApplicationWithClusterDir):
176 class IPControllerApp(ApplicationWithClusterDir):
177
177
178 name = 'ipcontroller'
178 name = 'ipcontroller'
179 description = 'Start the IPython controller for parallel computing.'
179 description = 'Start the IPython controller for parallel computing.'
180 config_file_name = default_config_file_name
180 config_file_name = default_config_file_name
181 auto_create_cluster_dir = True
181
182
182 def create_default_config(self):
183 def create_default_config(self):
183 super(IPControllerApp, self).create_default_config()
184 super(IPControllerApp, self).create_default_config()
184 self.default_config.Global.reuse_furls = False
185 self.default_config.Global.reuse_furls = False
185 self.default_config.Global.secure = True
186 self.default_config.Global.secure = True
186 self.default_config.Global.import_statements = []
187 self.default_config.Global.import_statements = []
187 self.default_config.Global.log_to_file = False
188 self.default_config.Global.log_to_file = False
188
189
189 def create_command_line_config(self):
190 def create_command_line_config(self):
190 """Create and return a command line config loader."""
191 """Create and return a command line config loader."""
191 return IPControllerAppCLConfigLoader(
192 return IPControllerAppCLConfigLoader(
192 description=self.description,
193 description=self.description,
193 version=release.version
194 version=release.version
194 )
195 )
195
196
196 def post_load_command_line_config(self):
197 def post_load_command_line_config(self):
197 # Now setup reuse_furls
198 # Now setup reuse_furls
198 c = self.command_line_config
199 c = self.command_line_config
199 if hasattr(c.Global, 'reuse_furls'):
200 if hasattr(c.Global, 'reuse_furls'):
200 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
201 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
201 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
202 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
202 del c.Global.reuse_furls
203 del c.Global.reuse_furls
203 if hasattr(c.Global, 'secure'):
204 if hasattr(c.Global, 'secure'):
204 c.FCClientServiceFactory.secure = c.Global.secure
205 c.FCClientServiceFactory.secure = c.Global.secure
205 c.FCEngineServiceFactory.secure = c.Global.secure
206 c.FCEngineServiceFactory.secure = c.Global.secure
206 del c.Global.secure
207 del c.Global.secure
207
208
208 def pre_construct(self):
209 def pre_construct(self):
209 # The log and security dirs were set earlier, but here we put them
210 # The log and security dirs were set earlier, but here we put them
210 # into the config and log them.
211 # into the config and log them.
211 config = self.master_config
212 config = self.master_config
212 sdir = self.cluster_dir_obj.security_dir
213 sdir = self.cluster_dir_obj.security_dir
213 self.security_dir = config.Global.security_dir = sdir
214 self.security_dir = config.Global.security_dir = sdir
214 ldir = self.cluster_dir_obj.log_dir
215 ldir = self.cluster_dir_obj.log_dir
215 self.log_dir = config.Global.log_dir = ldir
216 self.log_dir = config.Global.log_dir = ldir
216 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
217 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
217 self.log.info("Log directory set to: %s" % self.log_dir)
218 self.log.info("Log directory set to: %s" % self.log_dir)
218 self.log.info("Security directory set to: %s" % self.security_dir)
219 self.log.info("Security directory set to: %s" % self.security_dir)
219
220
220 def construct(self):
221 def construct(self):
221 # I am a little hesitant to put these into InteractiveShell itself.
222 # I am a little hesitant to put these into InteractiveShell itself.
222 # But that might be the place for them
223 # But that might be the place for them
223 sys.path.insert(0, '')
224 sys.path.insert(0, '')
224
225
225 self.start_logging()
226 self.start_logging()
226 self.import_statements()
227 self.import_statements()
227
228
228 # Create the service hierarchy
229 # Create the service hierarchy
229 self.main_service = service.MultiService()
230 self.main_service = service.MultiService()
230 # The controller service
231 # The controller service
231 controller_service = controllerservice.ControllerService()
232 controller_service = controllerservice.ControllerService()
232 controller_service.setServiceParent(self.main_service)
233 controller_service.setServiceParent(self.main_service)
233 # The client tub and all its refereceables
234 # The client tub and all its refereceables
234 csfactory = FCClientServiceFactory(self.master_config, controller_service)
235 csfactory = FCClientServiceFactory(self.master_config, controller_service)
235 client_service = csfactory.create()
236 client_service = csfactory.create()
236 client_service.setServiceParent(self.main_service)
237 client_service.setServiceParent(self.main_service)
237 # The engine tub
238 # The engine tub
238 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
239 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
239 engine_service = esfactory.create()
240 engine_service = esfactory.create()
240 engine_service.setServiceParent(self.main_service)
241 engine_service.setServiceParent(self.main_service)
241
242
242 def start_logging(self):
243 def start_logging(self):
243 if self.master_config.Global.log_to_file:
244 if self.master_config.Global.log_to_file:
244 log_filename = self.name + '-' + str(os.getpid()) + '.log'
245 log_filename = self.name + '-' + str(os.getpid()) + '.log'
245 logfile = os.path.join(self.log_dir, log_filename)
246 logfile = os.path.join(self.log_dir, log_filename)
246 open_log_file = open(logfile, 'w')
247 open_log_file = open(logfile, 'w')
247 else:
248 else:
248 open_log_file = sys.stdout
249 open_log_file = sys.stdout
249 log.startLogging(open_log_file)
250 log.startLogging(open_log_file)
250
251
251 def import_statements(self):
252 def import_statements(self):
252 statements = self.master_config.Global.import_statements
253 statements = self.master_config.Global.import_statements
253 for s in statements:
254 for s in statements:
254 try:
255 try:
255 log.msg("Executing statement: '%s'" % s)
256 log.msg("Executing statement: '%s'" % s)
256 exec s in globals(), locals()
257 exec s in globals(), locals()
257 except:
258 except:
258 log.msg("Error running statement: %s" % s)
259 log.msg("Error running statement: %s" % s)
259
260
260 def start_app(self):
261 def start_app(self):
261 # Start the controller service and set things running
262 # Start the controller service and set things running
262 self.main_service.startService()
263 self.main_service.startService()
263 reactor.run()
264 reactor.run()
264
265
265
266
266 def launch_new_instance():
267 def launch_new_instance():
267 """Create and run the IPython controller"""
268 """Create and run the IPython controller"""
268 app = IPControllerApp()
269 app = IPControllerApp()
269 app.start()
270 app.start()
270
271
271
272
272 if __name__ == '__main__':
273 if __name__ == '__main__':
273 launch_new_instance()
274 launch_new_instance()
274
275
@@ -1,248 +1,249 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 import os
18 import os
19 import sys
19 import sys
20
20
21 from twisted.application import service
21 from twisted.application import service
22 from twisted.internet import reactor
22 from twisted.internet import reactor
23 from twisted.python import log
23 from twisted.python import log
24
24
25 from IPython.config.loader import NoConfigDefault
25 from IPython.config.loader import NoConfigDefault
26
26
27 from IPython.kernel.clusterdir import (
27 from IPython.kernel.clusterdir import (
28 ApplicationWithClusterDir,
28 ApplicationWithClusterDir,
29 AppWithClusterDirArgParseConfigLoader
29 AppWithClusterDirArgParseConfigLoader
30 )
30 )
31 from IPython.core import release
31 from IPython.core import release
32
32
33 from IPython.utils.importstring import import_item
33 from IPython.utils.importstring import import_item
34
34
35 from IPython.kernel.engineservice import EngineService
35 from IPython.kernel.engineservice import EngineService
36 from IPython.kernel.fcutil import Tub
36 from IPython.kernel.fcutil import Tub
37 from IPython.kernel.engineconnector import EngineConnector
37 from IPython.kernel.engineconnector import EngineConnector
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # The main application
40 # The main application
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43
43
44 cl_args = (
44 cl_args = (
45 # Controller config
45 # Controller config
46 (('--furl-file',), dict(
46 (('--furl-file',), dict(
47 type=str, dest='Global.furl_file', default=NoConfigDefault,
47 type=str, dest='Global.furl_file', default=NoConfigDefault,
48 help='The full location of the file containing the FURL of the '
48 help='The full location of the file containing the FURL of the '
49 'controller. If this is not given, the FURL file must be in the '
49 'controller. If this is not given, the FURL file must be in the '
50 'security directory of the cluster directory. This location is '
50 'security directory of the cluster directory. This location is '
51 'resolved using the --profile and --app-dir options.',
51 'resolved using the --profile and --app-dir options.',
52 metavar='Global.furl_file')
52 metavar='Global.furl_file')
53 ),
53 ),
54 # MPI
54 # MPI
55 (('--mpi',), dict(
55 (('--mpi',), dict(
56 type=str, dest='MPI.use', default=NoConfigDefault,
56 type=str, dest='MPI.use', default=NoConfigDefault,
57 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).',
57 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).',
58 metavar='MPI.use')
58 metavar='MPI.use')
59 ),
59 ),
60 # Global config
60 # Global config
61 (('--log-to-file',), dict(
61 (('--log-to-file',), dict(
62 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
62 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
63 help='Log to a file in the log directory (default is stdout)')
63 help='Log to a file in the log directory (default is stdout)')
64 )
64 )
65 )
65 )
66
66
67
67
68 class IPEngineAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
68 class IPEngineAppCLConfigLoader(AppWithClusterDirArgParseConfigLoader):
69
69
70 arguments = cl_args
70 arguments = cl_args
71
71
72
72
73 mpi4py_init = """from mpi4py import MPI as mpi
73 mpi4py_init = """from mpi4py import MPI as mpi
74 mpi.size = mpi.COMM_WORLD.Get_size()
74 mpi.size = mpi.COMM_WORLD.Get_size()
75 mpi.rank = mpi.COMM_WORLD.Get_rank()
75 mpi.rank = mpi.COMM_WORLD.Get_rank()
76 """
76 """
77
77
78 pytrilinos_init = """from PyTrilinos import Epetra
78 pytrilinos_init = """from PyTrilinos import Epetra
79 class SimpleStruct:
79 class SimpleStruct:
80 pass
80 pass
81 mpi = SimpleStruct()
81 mpi = SimpleStruct()
82 mpi.rank = 0
82 mpi.rank = 0
83 mpi.size = 0
83 mpi.size = 0
84 """
84 """
85
85
86
86
87 default_config_file_name = 'ipengine_config.py'
87 default_config_file_name = 'ipengine_config.py'
88
88
89
89
90 class IPEngineApp(ApplicationWithClusterDir):
90 class IPEngineApp(ApplicationWithClusterDir):
91
91
92 name = 'ipengine'
92 name = 'ipengine'
93 description = 'Start the IPython engine for parallel computing.'
93 description = 'Start the IPython engine for parallel computing.'
94 config_file_name = default_config_file_name
94 config_file_name = default_config_file_name
95 auto_create_cluster_dir = True
95
96
96 def create_default_config(self):
97 def create_default_config(self):
97 super(IPEngineApp, self).create_default_config()
98 super(IPEngineApp, self).create_default_config()
98
99
99 # Global config attributes
100 # Global config attributes
100 self.default_config.Global.log_to_file = False
101 self.default_config.Global.log_to_file = False
101 self.default_config.Global.exec_lines = []
102 self.default_config.Global.exec_lines = []
102 # The log and security dir names must match that of the controller
103 # The log and security dir names must match that of the controller
103 self.default_config.Global.log_dir_name = 'log'
104 self.default_config.Global.log_dir_name = 'log'
104 self.default_config.Global.security_dir_name = 'security'
105 self.default_config.Global.security_dir_name = 'security'
105 self.default_config.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
106 self.default_config.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
106
107
107 # Configuration related to the controller
108 # Configuration related to the controller
108 # This must match the filename (path not included) that the controller
109 # This must match the filename (path not included) that the controller
109 # used for the FURL file.
110 # used for the FURL file.
110 self.default_config.Global.furl_file_name = 'ipcontroller-engine.furl'
111 self.default_config.Global.furl_file_name = 'ipcontroller-engine.furl'
111 # If given, this is the actual location of the controller's FURL file.
112 # If given, this is the actual location of the controller's FURL file.
112 # If not, this is computed using the profile, app_dir and furl_file_name
113 # If not, this is computed using the profile, app_dir and furl_file_name
113 self.default_config.Global.furl_file = ''
114 self.default_config.Global.furl_file = ''
114
115
115 # MPI related config attributes
116 # MPI related config attributes
116 self.default_config.MPI.use = ''
117 self.default_config.MPI.use = ''
117 self.default_config.MPI.mpi4py = mpi4py_init
118 self.default_config.MPI.mpi4py = mpi4py_init
118 self.default_config.MPI.pytrilinos = pytrilinos_init
119 self.default_config.MPI.pytrilinos = pytrilinos_init
119
120
120 def create_command_line_config(self):
121 def create_command_line_config(self):
121 """Create and return a command line config loader."""
122 """Create and return a command line config loader."""
122 return IPEngineAppCLConfigLoader(
123 return IPEngineAppCLConfigLoader(
123 description=self.description,
124 description=self.description,
124 version=release.version
125 version=release.version
125 )
126 )
126
127
127 def post_load_command_line_config(self):
128 def post_load_command_line_config(self):
128 pass
129 pass
129
130
130 def pre_construct(self):
131 def pre_construct(self):
131 config = self.master_config
132 config = self.master_config
132 sdir = self.cluster_dir_obj.security_dir
133 sdir = self.cluster_dir_obj.security_dir
133 self.security_dir = config.Global.security_dir = sdir
134 self.security_dir = config.Global.security_dir = sdir
134 ldir = self.cluster_dir_obj.log_dir
135 ldir = self.cluster_dir_obj.log_dir
135 self.log_dir = config.Global.log_dir = ldir
136 self.log_dir = config.Global.log_dir = ldir
136 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
137 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
137 self.log.info("Log directory set to: %s" % self.log_dir)
138 self.log.info("Log directory set to: %s" % self.log_dir)
138 self.log.info("Security directory set to: %s" % self.security_dir)
139 self.log.info("Security directory set to: %s" % self.security_dir)
139
140
140 self.find_cont_furl_file()
141 self.find_cont_furl_file()
141
142
142 def find_cont_furl_file(self):
143 def find_cont_furl_file(self):
143 """Set the furl file.
144 """Set the furl file.
144
145
145 Here we don't try to actually see if it exists for is valid as that
146 Here we don't try to actually see if it exists for is valid as that
146 is hadled by the connection logic.
147 is hadled by the connection logic.
147 """
148 """
148 config = self.master_config
149 config = self.master_config
149 # Find the actual controller FURL file
150 # Find the actual controller FURL file
150 if not config.Global.furl_file:
151 if not config.Global.furl_file:
151 try_this = os.path.join(
152 try_this = os.path.join(
152 config.Global.cluster_dir,
153 config.Global.cluster_dir,
153 config.Global.security_dir,
154 config.Global.security_dir,
154 config.Global.furl_file_name
155 config.Global.furl_file_name
155 )
156 )
156 config.Global.furl_file = try_this
157 config.Global.furl_file = try_this
157
158
158 def construct(self):
159 def construct(self):
159 # I am a little hesitant to put these into InteractiveShell itself.
160 # I am a little hesitant to put these into InteractiveShell itself.
160 # But that might be the place for them
161 # But that might be the place for them
161 sys.path.insert(0, '')
162 sys.path.insert(0, '')
162
163
163 self.start_mpi()
164 self.start_mpi()
164 self.start_logging()
165 self.start_logging()
165
166
166 # Create the underlying shell class and EngineService
167 # Create the underlying shell class and EngineService
167 shell_class = import_item(self.master_config.Global.shell_class)
168 shell_class = import_item(self.master_config.Global.shell_class)
168 self.engine_service = EngineService(shell_class, mpi=mpi)
169 self.engine_service = EngineService(shell_class, mpi=mpi)
169
170
170 self.exec_lines()
171 self.exec_lines()
171
172
172 # Create the service hierarchy
173 # Create the service hierarchy
173 self.main_service = service.MultiService()
174 self.main_service = service.MultiService()
174 self.engine_service.setServiceParent(self.main_service)
175 self.engine_service.setServiceParent(self.main_service)
175 self.tub_service = Tub()
176 self.tub_service = Tub()
176 self.tub_service.setServiceParent(self.main_service)
177 self.tub_service.setServiceParent(self.main_service)
177 # This needs to be called before the connection is initiated
178 # This needs to be called before the connection is initiated
178 self.main_service.startService()
179 self.main_service.startService()
179
180
180 # This initiates the connection to the controller and calls
181 # This initiates the connection to the controller and calls
181 # register_engine to tell the controller we are ready to do work
182 # register_engine to tell the controller we are ready to do work
182 self.engine_connector = EngineConnector(self.tub_service)
183 self.engine_connector = EngineConnector(self.tub_service)
183
184
184 log.msg("Using furl file: %s" % self.master_config.Global.furl_file)
185 log.msg("Using furl file: %s" % self.master_config.Global.furl_file)
185
186
186 reactor.callWhenRunning(self.call_connect)
187 reactor.callWhenRunning(self.call_connect)
187
188
188 def call_connect(self):
189 def call_connect(self):
189 d = self.engine_connector.connect_to_controller(
190 d = self.engine_connector.connect_to_controller(
190 self.engine_service,
191 self.engine_service,
191 self.master_config.Global.furl_file
192 self.master_config.Global.furl_file
192 )
193 )
193
194
194 def handle_error(f):
195 def handle_error(f):
195 log.msg('Error connecting to controller. This usually means that '
196 log.msg('Error connecting to controller. This usually means that '
196 'i) the controller was not started, ii) a firewall was blocking '
197 'i) the controller was not started, ii) a firewall was blocking '
197 'the engine from connecting to the controller or iii) the engine '
198 'the engine from connecting to the controller or iii) the engine '
198 ' was not pointed at the right FURL file:')
199 ' was not pointed at the right FURL file:')
199 log.msg(f.getErrorMessage())
200 log.msg(f.getErrorMessage())
200 reactor.callLater(0.1, reactor.stop)
201 reactor.callLater(0.1, reactor.stop)
201
202
202 d.addErrback(handle_error)
203 d.addErrback(handle_error)
203
204
204 def start_mpi(self):
205 def start_mpi(self):
205 global mpi
206 global mpi
206 mpikey = self.master_config.MPI.use
207 mpikey = self.master_config.MPI.use
207 mpi_import_statement = self.master_config.MPI.get(mpikey, None)
208 mpi_import_statement = self.master_config.MPI.get(mpikey, None)
208 if mpi_import_statement is not None:
209 if mpi_import_statement is not None:
209 try:
210 try:
210 self.log.info("Initializing MPI:")
211 self.log.info("Initializing MPI:")
211 self.log.info(mpi_import_statement)
212 self.log.info(mpi_import_statement)
212 exec mpi_import_statement in globals()
213 exec mpi_import_statement in globals()
213 except:
214 except:
214 mpi = None
215 mpi = None
215 else:
216 else:
216 mpi = None
217 mpi = None
217
218
218 def start_logging(self):
219 def start_logging(self):
219 if self.master_config.Global.log_to_file:
220 if self.master_config.Global.log_to_file:
220 log_filename = self.name + '-' + str(os.getpid()) + '.log'
221 log_filename = self.name + '-' + str(os.getpid()) + '.log'
221 logfile = os.path.join(self.log_dir, log_filename)
222 logfile = os.path.join(self.log_dir, log_filename)
222 open_log_file = open(logfile, 'w')
223 open_log_file = open(logfile, 'w')
223 else:
224 else:
224 open_log_file = sys.stdout
225 open_log_file = sys.stdout
225 log.startLogging(open_log_file)
226 log.startLogging(open_log_file)
226
227
227 def exec_lines(self):
228 def exec_lines(self):
228 for line in self.master_config.Global.exec_lines:
229 for line in self.master_config.Global.exec_lines:
229 try:
230 try:
230 log.msg("Executing statement: '%s'" % line)
231 log.msg("Executing statement: '%s'" % line)
231 self.engine_service.execute(line)
232 self.engine_service.execute(line)
232 except:
233 except:
233 log.msg("Error executing statement: %s" % line)
234 log.msg("Error executing statement: %s" % line)
234
235
235 def start_app(self):
236 def start_app(self):
236 # Start the controller service and set things running
237 # Start the controller service and set things running
237 reactor.run()
238 reactor.run()
238
239
239
240
240 def launch_new_instance():
241 def launch_new_instance():
241 """Create and run the IPython controller"""
242 """Create and run the IPython controller"""
242 app = IPEngineApp()
243 app = IPEngineApp()
243 app.start()
244 app.start()
244
245
245
246
246 if __name__ == '__main__':
247 if __name__ == '__main__':
247 launch_new_instance()
248 launch_new_instance()
248
249
@@ -1,256 +1,265 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """Things directly related to all of twisted."""
4 """Things directly related to all of twisted."""
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 import os, sys
19 import os, sys
20 import threading, Queue, atexit
20 import threading, Queue, atexit
21
21
22 import twisted
22 import twisted
23 from twisted.internet import defer, reactor
23 from twisted.internet import defer, reactor
24 from twisted.python import log, failure
24 from twisted.python import log, failure
25
25
26 from IPython.kernel.error import FileTimeoutError
26 from IPython.kernel.error import FileTimeoutError
27
27
28 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
29 # Classes related to twisted and threads
29 # Classes related to twisted and threads
30 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
31
31
32
32
33 class ReactorInThread(threading.Thread):
33 class ReactorInThread(threading.Thread):
34 """Run the twisted reactor in a different thread.
34 """Run the twisted reactor in a different thread.
35
35
36 For the process to be able to exit cleanly, do the following:
36 For the process to be able to exit cleanly, do the following:
37
37
38 rit = ReactorInThread()
38 rit = ReactorInThread()
39 rit.setDaemon(True)
39 rit.setDaemon(True)
40 rit.start()
40 rit.start()
41
41
42 """
42 """
43
43
44 def run(self):
44 def run(self):
45 reactor.run(installSignalHandlers=0)
45 reactor.run(installSignalHandlers=0)
46 # self.join()
46 # self.join()
47
47
48 def stop(self):
48 def stop(self):
49 # I don't think this does anything useful.
49 # I don't think this does anything useful.
50 blockingCallFromThread(reactor.stop)
50 blockingCallFromThread(reactor.stop)
51 self.join()
51 self.join()
52
52
53 if(twisted.version.major >= 8):
53 if(twisted.version.major >= 8):
54 import twisted.internet.threads
54 import twisted.internet.threads
55 def blockingCallFromThread(f, *a, **kw):
55 def blockingCallFromThread(f, *a, **kw):
56 """
56 """
57 Run a function in the reactor from a thread, and wait for the result
57 Run a function in the reactor from a thread, and wait for the result
58 synchronously, i.e. until the callback chain returned by the function get a
58 synchronously, i.e. until the callback chain returned by the function get a
59 result.
59 result.
60
60
61 Delegates to twisted.internet.threads.blockingCallFromThread(reactor, f, *a, **kw),
61 Delegates to twisted.internet.threads.blockingCallFromThread(reactor, f, *a, **kw),
62 passing twisted.internet.reactor for the first argument.
62 passing twisted.internet.reactor for the first argument.
63
63
64 @param f: the callable to run in the reactor thread
64 @param f: the callable to run in the reactor thread
65 @type f: any callable.
65 @type f: any callable.
66 @param a: the arguments to pass to C{f}.
66 @param a: the arguments to pass to C{f}.
67 @param kw: the keyword arguments to pass to C{f}.
67 @param kw: the keyword arguments to pass to C{f}.
68
68
69 @return: the result of the callback chain.
69 @return: the result of the callback chain.
70 @raise: any error raised during the callback chain.
70 @raise: any error raised during the callback chain.
71 """
71 """
72 return twisted.internet.threads.blockingCallFromThread(reactor, f, *a, **kw)
72 return twisted.internet.threads.blockingCallFromThread(reactor, f, *a, **kw)
73
73
74 else:
74 else:
75 def blockingCallFromThread(f, *a, **kw):
75 def blockingCallFromThread(f, *a, **kw):
76 """
76 """
77 Run a function in the reactor from a thread, and wait for the result
77 Run a function in the reactor from a thread, and wait for the result
78 synchronously, i.e. until the callback chain returned by the function get a
78 synchronously, i.e. until the callback chain returned by the function get a
79 result.
79 result.
80
80
81 @param f: the callable to run in the reactor thread
81 @param f: the callable to run in the reactor thread
82 @type f: any callable.
82 @type f: any callable.
83 @param a: the arguments to pass to C{f}.
83 @param a: the arguments to pass to C{f}.
84 @param kw: the keyword arguments to pass to C{f}.
84 @param kw: the keyword arguments to pass to C{f}.
85
85
86 @return: the result of the callback chain.
86 @return: the result of the callback chain.
87 @raise: any error raised during the callback chain.
87 @raise: any error raised during the callback chain.
88 """
88 """
89 from twisted.internet import reactor
89 from twisted.internet import reactor
90 queue = Queue.Queue()
90 queue = Queue.Queue()
91 def _callFromThread():
91 def _callFromThread():
92 result = defer.maybeDeferred(f, *a, **kw)
92 result = defer.maybeDeferred(f, *a, **kw)
93 result.addBoth(queue.put)
93 result.addBoth(queue.put)
94
94
95 reactor.callFromThread(_callFromThread)
95 reactor.callFromThread(_callFromThread)
96 result = queue.get()
96 result = queue.get()
97 if isinstance(result, failure.Failure):
97 if isinstance(result, failure.Failure):
98 # This makes it easier for the debugger to get access to the instance
98 # This makes it easier for the debugger to get access to the instance
99 try:
99 try:
100 result.raiseException()
100 result.raiseException()
101 except Exception, e:
101 except Exception, e:
102 raise e
102 raise e
103 return result
103 return result
104
104
105
105
106
106
107 #-------------------------------------------------------------------------------
107 #-------------------------------------------------------------------------------
108 # Things for managing deferreds
108 # Things for managing deferreds
109 #-------------------------------------------------------------------------------
109 #-------------------------------------------------------------------------------
110
110
111
111
112 def parseResults(results):
112 def parseResults(results):
113 """Pull out results/Failures from a DeferredList."""
113 """Pull out results/Failures from a DeferredList."""
114 return [x[1] for x in results]
114 return [x[1] for x in results]
115
115
116 def gatherBoth(dlist, fireOnOneCallback=0,
116 def gatherBoth(dlist, fireOnOneCallback=0,
117 fireOnOneErrback=0,
117 fireOnOneErrback=0,
118 consumeErrors=0,
118 consumeErrors=0,
119 logErrors=0):
119 logErrors=0):
120 """This is like gatherBoth, but sets consumeErrors=1."""
120 """This is like gatherBoth, but sets consumeErrors=1."""
121 d = DeferredList(dlist, fireOnOneCallback, fireOnOneErrback,
121 d = DeferredList(dlist, fireOnOneCallback, fireOnOneErrback,
122 consumeErrors, logErrors)
122 consumeErrors, logErrors)
123 if not fireOnOneCallback:
123 if not fireOnOneCallback:
124 d.addCallback(parseResults)
124 d.addCallback(parseResults)
125 return d
125 return d
126
126
127 SUCCESS = True
127 SUCCESS = True
128 FAILURE = False
128 FAILURE = False
129
129
130 class DeferredList(defer.Deferred):
130 class DeferredList(defer.Deferred):
131 """I combine a group of deferreds into one callback.
131 """I combine a group of deferreds into one callback.
132
132
133 I track a list of L{Deferred}s for their callbacks, and make a single
133 I track a list of L{Deferred}s for their callbacks, and make a single
134 callback when they have all completed, a list of (success, result)
134 callback when they have all completed, a list of (success, result)
135 tuples, 'success' being a boolean.
135 tuples, 'success' being a boolean.
136
136
137 Note that you can still use a L{Deferred} after putting it in a
137 Note that you can still use a L{Deferred} after putting it in a
138 DeferredList. For example, you can suppress 'Unhandled error in Deferred'
138 DeferredList. For example, you can suppress 'Unhandled error in Deferred'
139 messages by adding errbacks to the Deferreds *after* putting them in the
139 messages by adding errbacks to the Deferreds *after* putting them in the
140 DeferredList, as a DeferredList won't swallow the errors. (Although a more
140 DeferredList, as a DeferredList won't swallow the errors. (Although a more
141 convenient way to do this is simply to set the consumeErrors flag)
141 convenient way to do this is simply to set the consumeErrors flag)
142
142
143 Note: This is a modified version of the twisted.internet.defer.DeferredList
143 Note: This is a modified version of the twisted.internet.defer.DeferredList
144 """
144 """
145
145
146 fireOnOneCallback = 0
146 fireOnOneCallback = 0
147 fireOnOneErrback = 0
147 fireOnOneErrback = 0
148
148
149 def __init__(self, deferredList, fireOnOneCallback=0, fireOnOneErrback=0,
149 def __init__(self, deferredList, fireOnOneCallback=0, fireOnOneErrback=0,
150 consumeErrors=0, logErrors=0):
150 consumeErrors=0, logErrors=0):
151 """Initialize a DeferredList.
151 """Initialize a DeferredList.
152
152
153 @type deferredList: C{list} of L{Deferred}s
153 @type deferredList: C{list} of L{Deferred}s
154 @param deferredList: The list of deferreds to track.
154 @param deferredList: The list of deferreds to track.
155 @param fireOnOneCallback: (keyword param) a flag indicating that
155 @param fireOnOneCallback: (keyword param) a flag indicating that
156 only one callback needs to be fired for me to call
156 only one callback needs to be fired for me to call
157 my callback
157 my callback
158 @param fireOnOneErrback: (keyword param) a flag indicating that
158 @param fireOnOneErrback: (keyword param) a flag indicating that
159 only one errback needs to be fired for me to call
159 only one errback needs to be fired for me to call
160 my errback
160 my errback
161 @param consumeErrors: (keyword param) a flag indicating that any errors
161 @param consumeErrors: (keyword param) a flag indicating that any errors
162 raised in the original deferreds should be
162 raised in the original deferreds should be
163 consumed by this DeferredList. This is useful to
163 consumed by this DeferredList. This is useful to
164 prevent spurious warnings being logged.
164 prevent spurious warnings being logged.
165 """
165 """
166 self.resultList = [None] * len(deferredList)
166 self.resultList = [None] * len(deferredList)
167 defer.Deferred.__init__(self)
167 defer.Deferred.__init__(self)
168 if len(deferredList) == 0 and not fireOnOneCallback:
168 if len(deferredList) == 0 and not fireOnOneCallback:
169 self.callback(self.resultList)
169 self.callback(self.resultList)
170
170
171 # These flags need to be set *before* attaching callbacks to the
171 # These flags need to be set *before* attaching callbacks to the
172 # deferreds, because the callbacks use these flags, and will run
172 # deferreds, because the callbacks use these flags, and will run
173 # synchronously if any of the deferreds are already fired.
173 # synchronously if any of the deferreds are already fired.
174 self.fireOnOneCallback = fireOnOneCallback
174 self.fireOnOneCallback = fireOnOneCallback
175 self.fireOnOneErrback = fireOnOneErrback
175 self.fireOnOneErrback = fireOnOneErrback
176 self.consumeErrors = consumeErrors
176 self.consumeErrors = consumeErrors
177 self.logErrors = logErrors
177 self.logErrors = logErrors
178 self.finishedCount = 0
178 self.finishedCount = 0
179
179
180 index = 0
180 index = 0
181 for deferred in deferredList:
181 for deferred in deferredList:
182 deferred.addCallbacks(self._cbDeferred, self._cbDeferred,
182 deferred.addCallbacks(self._cbDeferred, self._cbDeferred,
183 callbackArgs=(index,SUCCESS),
183 callbackArgs=(index,SUCCESS),
184 errbackArgs=(index,FAILURE))
184 errbackArgs=(index,FAILURE))
185 index = index + 1
185 index = index + 1
186
186
187 def _cbDeferred(self, result, index, succeeded):
187 def _cbDeferred(self, result, index, succeeded):
188 """(internal) Callback for when one of my deferreds fires.
188 """(internal) Callback for when one of my deferreds fires.
189 """
189 """
190 self.resultList[index] = (succeeded, result)
190 self.resultList[index] = (succeeded, result)
191
191
192 self.finishedCount += 1
192 self.finishedCount += 1
193 if not self.called:
193 if not self.called:
194 if succeeded == SUCCESS and self.fireOnOneCallback:
194 if succeeded == SUCCESS and self.fireOnOneCallback:
195 self.callback((result, index))
195 self.callback((result, index))
196 elif succeeded == FAILURE and self.fireOnOneErrback:
196 elif succeeded == FAILURE and self.fireOnOneErrback:
197 # We have modified this to fire the errback chain with the actual
197 # We have modified this to fire the errback chain with the actual
198 # Failure instance the originally occured rather than twisted's
198 # Failure instance the originally occured rather than twisted's
199 # FirstError which wraps the failure
199 # FirstError which wraps the failure
200 self.errback(result)
200 self.errback(result)
201 elif self.finishedCount == len(self.resultList):
201 elif self.finishedCount == len(self.resultList):
202 self.callback(self.resultList)
202 self.callback(self.resultList)
203
203
204 if succeeded == FAILURE and self.logErrors:
204 if succeeded == FAILURE and self.logErrors:
205 log.err(result)
205 log.err(result)
206 if succeeded == FAILURE and self.consumeErrors:
206 if succeeded == FAILURE and self.consumeErrors:
207 result = None
207 result = None
208
208
209 return result
209 return result
210
210
211
211
212 def wait_for_file(filename, delay=0.1, max_tries=10):
212 def wait_for_file(filename, delay=0.1, max_tries=10):
213 """Wait (poll) for a file to be created.
213 """Wait (poll) for a file to be created.
214
214
215 This method returns a Deferred that will fire when a file exists. It
215 This method returns a Deferred that will fire when a file exists. It
216 works by polling os.path.isfile in time intervals specified by the
216 works by polling os.path.isfile in time intervals specified by the
217 delay argument. If `max_tries` is reached, it will errback with a
217 delay argument. If `max_tries` is reached, it will errback with a
218 `FileTimeoutError`.
218 `FileTimeoutError`.
219
219
220 Parameters
220 Parameters
221 ----------
221 ----------
222 filename : str
222 filename : str
223 The name of the file to wait for.
223 The name of the file to wait for.
224 delay : float
224 delay : float
225 The time to wait between polls.
225 The time to wait between polls.
226 max_tries : int
226 max_tries : int
227 The max number of attempts before raising `FileTimeoutError`
227 The max number of attempts before raising `FileTimeoutError`
228
228
229 Returns
229 Returns
230 -------
230 -------
231 d : Deferred
231 d : Deferred
232 A Deferred instance that will fire when the file exists.
232 A Deferred instance that will fire when the file exists.
233 """
233 """
234
234
235 d = defer.Deferred()
235 d = defer.Deferred()
236
236
237 def _test_for_file(filename, attempt=0):
237 def _test_for_file(filename, attempt=0):
238 if attempt >= max_tries:
238 if attempt >= max_tries:
239 d.errback(FileTimeoutError(
239 d.errback(FileTimeoutError(
240 'timeout waiting for file to be created: %s' % filename
240 'timeout waiting for file to be created: %s' % filename
241 ))
241 ))
242 else:
242 else:
243 if os.path.isfile(filename):
243 if os.path.isfile(filename):
244 d.callback(True)
244 d.callback(True)
245 else:
245 else:
246 reactor.callLater(delay, _test_for_file, filename, attempt+1)
246 reactor.callLater(delay, _test_for_file, filename, attempt+1)
247
247
248 _test_for_file(filename)
248 _test_for_file(filename)
249 return d
249 return d
250
250
251
251
252 def sleep_deferred(seconds):
252 def sleep_deferred(seconds):
253 """Sleep without blocking the event loop."""
253 """Sleep without blocking the event loop."""
254 d = defer.Deferred()
254 d = defer.Deferred()
255 reactor.callLater(seconds, d.callback, seconds)
255 reactor.callLater(seconds, d.callback, seconds)
256 return d
256 return d
257
258
259 def make_deferred(func):
260 """A decorator that calls a function with :func`maybeDeferred`."""
261
262 def _wrapper(*args, **kwargs):
263 return defer.maybeDeferred(func, *args, **kwargs)
264
265 return _wrapper No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now