##// END OF EJS Templates
General work on the kernel config.
Brian Granger -
Show More
@@ -1,60 +1,67 b''
1 1 from IPython.config.loader import Config
2 2
3 3 c = get_config()
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Global configuration
7 7 #-----------------------------------------------------------------------------
8 8
9 c.Global.logfile = ''
10 c.Global.import_statement = ''
9 c.Global.log_to_file = False
10 c.Global.import_statements = []
11 11 c.Global.reuse_furls = False
12 12
13 # You shouldn't have to edit these
14 c.Global.log_dir_name = 'log'
15 c.Global.security_dir_name = 'security'
16
13 17
14 18 #-----------------------------------------------------------------------------
15 19 # Configure the client services
16 20 #-----------------------------------------------------------------------------
17 21
18 22 c.FCClientServiceFactory.ip = ''
19 23 c.FCClientServiceFactory.port = 0
20 24 c.FCClientServiceFactory.location = ''
21 25 c.FCClientServiceFactory.secure = True
26 c.FCClientServiceFactory.reuse_furls = False
22 27 c.FCClientServiceFactory.cert_file = 'ipcontroller-client.pem'
23 28
24 29 c.FCClientServiceFactory.Interfaces.Task.interface_chain = [
25 30 'IPython.kernel.task.ITaskController',
26 31 'IPython.kernel.taskfc.IFCTaskController'
27 32 ]
33 # This is just the filename of the furl file. The path is always the
34 # security dir of the cluster directory.
28 35 c.FCClientServiceFactory.Interfaces.Task.furl_file = 'ipcontroller-tc.furl'
29 36
30 37 c.FCClientServiceFactory.Interfaces.MultiEngine.interface_chain = [
31 38 'IPython.kernel.multiengine.IMultiEngine',
32 39 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
33 40 ]
41 # This is just the filename of the furl file. The path is always the
42 # security dir of the cluster directory.
34 43 c.FCClientServiceFactory.Interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
35 44
36 45
37 46 #-----------------------------------------------------------------------------
38 47 # Configure the engine services
39 48 #-----------------------------------------------------------------------------
40 49
41 50 c.FCEngineServiceFactory.ip = ''
42 51 c.FCEngineServiceFactory.port = 0
43 52 c.FCEngineServiceFactory.location = ''
44 53 c.FCEngineServiceFactory.secure = True
54 c.FCEngineServiceFactory.reuse_furls = False
45 55 c.FCEngineServiceFactory.cert_file = 'ipcontroller-engine.pem'
46 56
47 engine_config = Config()
48 engine_config.furl_file =
49 c.Global.engine_furl_file = 'ipcontroller-engine.furl'
50 c.Global.engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase'
51
57 c.FCEngineServiceFactory.Intefaces.Default.interface_chain = [
58 'IPython.kernel.enginefc.IFCControllerBase'
59 ]
52 60
61 # This is just the filename of the furl file. The path is always the
62 # security dir of the cluster directory.
63 c.FCEngineServiceFactory.Intefaces.Default.furl_file = 'ipcontroller-engine.furl'
53 64
54 65
55 66
56 CLIENT_INTERFACES = dict(
57 TASK = dict(FURL_FILE = 'ipcontroller-tc.furl'),
58 MULTIENGINE = dict(FURLFILE='ipcontroller-mec.furl')
59 )
60 67
@@ -1,304 +1,334 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 An application for IPython
5 5
6 6 Authors:
7 7
8 8 * Brian Granger
9 9 * Fernando Perez
10 10
11 11 Notes
12 12 -----
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2009 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 import logging
27 27 import os
28 28 import sys
29 29 import traceback
30 30 from copy import deepcopy
31 31
32 32 from IPython.utils.genutils import get_ipython_dir, filefind
33 33 from IPython.config.loader import (
34 34 PyFileConfigLoader,
35 35 ArgParseConfigLoader,
36 36 Config,
37 37 NoConfigDefault
38 38 )
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Classes and functions
42 42 #-----------------------------------------------------------------------------
43 43
44 44
45 45 class IPythonArgParseConfigLoader(ArgParseConfigLoader):
46 46 """Default command line options for IPython based applications."""
47 47
48 48 def _add_other_arguments(self):
49 self.parser.add_argument('-ipythondir', '--ipythondir',
49 self.parser.add_argument('-ipythondir', '--ipython-dir',
50 50 dest='Global.ipythondir',type=str,
51 51 help='Set to override default location of Global.ipythondir.',
52 52 default=NoConfigDefault,
53 53 metavar='Global.ipythondir')
54 54 self.parser.add_argument('-p','-profile', '--profile',
55 55 dest='Global.profile',type=str,
56 56 help='The string name of the ipython profile to be used.',
57 57 default=NoConfigDefault,
58 58 metavar='Global.profile')
59 59 self.parser.add_argument('-log_level', '--log-level',
60 60 dest="Global.log_level",type=int,
61 61 help='Set the log level (0,10,20,30,40,50). Default is 30.',
62 62 default=NoConfigDefault)
63 63 self.parser.add_argument('-config_file', '--config-file',
64 64 dest='Global.config_file',type=str,
65 65 help='Set the config file name to override default.',
66 66 default=NoConfigDefault,
67 67 metavar='Global.config_file')
68 68
69 69
70 70 class ApplicationError(Exception):
71 71 pass
72 72
73 73
74 74 class Application(object):
75 75 """Load a config, construct an app and run it.
76 76 """
77 77
78 78 config_file_name = 'ipython_config.py'
79 79 name = 'ipython'
80 default_log_level = logging.WARN
81
80 82
81 83 def __init__(self):
82 84 self.init_logger()
83 85 self.default_config_file_name = self.config_file_name
84 86
85 87 def init_logger(self):
86 88 self.log = logging.getLogger(self.__class__.__name__)
87 89 # This is used as the default until the command line arguments are read.
88 self.log.setLevel(logging.WARN)
90 self.log.setLevel(self.default_log_level)
89 91 self._log_handler = logging.StreamHandler()
90 92 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
91 93 self._log_handler.setFormatter(self._log_formatter)
92 94 self.log.addHandler(self._log_handler)
93 95
94 96 def _set_log_level(self, level):
95 97 self.log.setLevel(level)
96 98
97 99 def _get_log_level(self):
98 100 return self.log.level
99 101
100 102 log_level = property(_get_log_level, _set_log_level)
101 103
102 104 def start(self):
103 105 """Start the application."""
104 106 self.attempt(self.create_default_config)
107 self.log_default_config()
108 self.set_default_config_log_level()
105 109 self.attempt(self.pre_load_command_line_config)
106 110 self.attempt(self.load_command_line_config, action='abort')
111 self.set_command_line_config_log_level()
107 112 self.attempt(self.post_load_command_line_config)
113 self.log_command_line_config()
108 114 self.attempt(self.find_ipythondir)
109 115 self.attempt(self.find_config_file_name)
110 116 self.attempt(self.find_config_file_paths)
111 117 self.attempt(self.pre_load_file_config)
112 118 self.attempt(self.load_file_config)
119 self.set_file_config_log_level()
113 120 self.attempt(self.post_load_file_config)
121 self.log_file_config()
114 122 self.attempt(self.merge_configs)
123 self.log_master_config()
115 124 self.attempt(self.pre_construct)
116 125 self.attempt(self.construct)
117 126 self.attempt(self.post_construct)
118 127 self.attempt(self.start_app)
119 128
120 129 #-------------------------------------------------------------------------
121 130 # Various stages of Application creation
122 131 #-------------------------------------------------------------------------
123 132
124 133 def create_default_config(self):
125 134 """Create defaults that can't be set elsewhere.
126 135
127 136 For the most part, we try to set default in the class attributes
128 137 of Components. But, defaults the top-level Application (which is
129 138 not a HasTraitlets or Component) are not set in this way. Instead
130 139 we set them here. The Global section is for variables like this that
131 140 don't belong to a particular component.
132 141 """
133 142 self.default_config = Config()
134 143 self.default_config.Global.ipythondir = get_ipython_dir()
144
145 def log_default_config(self):
135 146 self.log.debug('Default config loaded:')
136 147 self.log.debug(repr(self.default_config))
137 148
149 def set_default_config_log_level(self):
150 try:
151 self.log_level = self.default_config.Global.log_level
152 except AttributeError:
153 # Fallback to the default_log_level class attribute
154 pass
155
138 156 def create_command_line_config(self):
139 157 """Create and return a command line config loader."""
140 158 return IPythonArgParseConfigLoader(description=self.name)
141 159
142 160 def pre_load_command_line_config(self):
143 161 """Do actions just before loading the command line config."""
144 162 pass
145 163
146 164 def load_command_line_config(self):
147 """Load the command line config.
148
149 This method also sets ``self.debug``.
150 """
151
165 """Load the command line config."""
152 166 loader = self.create_command_line_config()
153 167 self.command_line_config = loader.load_config()
154 168 self.extra_args = loader.get_extra_args()
155 169
170 def set_command_line_config_log_level(self):
156 171 try:
157 172 self.log_level = self.command_line_config.Global.log_level
158 173 except AttributeError:
159 pass # Use existing value which is set in Application.init_logger.
160 self.log.debug("Command line config loaded:")
161 self.log.debug(repr(self.command_line_config))
174 pass
162 175
163 176 def post_load_command_line_config(self):
164 177 """Do actions just after loading the command line config."""
165 178 pass
166 179
180 def log_command_line_config(self):
181 self.log.debug("Command line config loaded:")
182 self.log.debug(repr(self.command_line_config))
183
167 184 def find_ipythondir(self):
168 185 """Set the IPython directory.
169 186
170 187 This sets ``self.ipythondir``, but the actual value that is passed
171 188 to the application is kept in either ``self.default_config`` or
172 189 ``self.command_line_config``. This also added ``self.ipythondir`` to
173 190 ``sys.path`` so config files there can be references by other config
174 191 files.
175 192 """
176 193
177 194 try:
178 195 self.ipythondir = self.command_line_config.Global.ipythondir
179 196 except AttributeError:
180 197 self.ipythondir = self.default_config.Global.ipythondir
181 198 sys.path.append(os.path.abspath(self.ipythondir))
182 199 if not os.path.isdir(self.ipythondir):
183 200 os.makedirs(self.ipythondir, mode = 0777)
184 201 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
185 202
186 203 def find_config_file_name(self):
187 204 """Find the config file name for this application.
188 205
206 This must set ``self.config_file_name`` to the filename of the
207 config file to use (just the filename). The search paths for the
208 config file are set in :meth:`find_config_file_paths` and then passed
209 to the config file loader where they are resolved to an absolute path.
210
189 211 If a profile has been set at the command line, this will resolve
190 it. The search paths for the config file are set in
191 :meth:`find_config_file_paths` and then passed to the config file
192 loader where they are resolved to an absolute path.
212 it.
193 213 """
194 214
195 215 try:
196 216 self.config_file_name = self.command_line_config.Global.config_file
197 217 except AttributeError:
198 218 pass
199 219
200 220 try:
201 221 self.profile_name = self.command_line_config.Global.profile
202 222 name_parts = self.config_file_name.split('.')
203 223 name_parts.insert(1, '_' + self.profile_name + '.')
204 224 self.config_file_name = ''.join(name_parts)
205 225 except AttributeError:
206 226 pass
207 227
208 228 def find_config_file_paths(self):
209 """Set the search paths for resolving the config file."""
229 """Set the search paths for resolving the config file.
230
231 This must set ``self.config_file_paths`` to a sequence of search
232 paths to pass to the config file loader.
233 """
210 234 self.config_file_paths = (os.getcwd(), self.ipythondir)
211 235
212 236 def pre_load_file_config(self):
213 237 """Do actions before the config file is loaded."""
214 238 pass
215 239
216 240 def load_file_config(self):
217 241 """Load the config file.
218 242
219 243 This tries to load the config file from disk. If successful, the
220 244 ``CONFIG_FILE`` config variable is set to the resolved config file
221 245 location. If not successful, an empty config is used.
222 246 """
223 247 self.log.debug("Attempting to load config file: <%s>" % self.config_file_name)
224 248 loader = PyFileConfigLoader(self.config_file_name,
225 249 path=self.config_file_paths)
226 250 try:
227 251 self.file_config = loader.load_config()
228 252 self.file_config.Global.config_file = loader.full_filename
229 253 except IOError:
230 254 # Only warn if the default config file was NOT being used.
231 255 if not self.config_file_name==self.default_config_file_name:
232 256 self.log.warn("Config file not found, skipping: <%s>" % \
233 257 self.config_file_name, exc_info=True)
234 258 self.file_config = Config()
235 259 except:
236 260 self.log.warn("Error loading config file: <%s>" % \
237 261 self.config_file_name, exc_info=True)
238 262 self.file_config = Config()
239 else:
240 self.log.debug("Config file loaded: <%s>" % loader.full_filename)
241 self.log.debug(repr(self.file_config))
263
264 def set_file_config_log_level(self):
242 265 # We need to keeep self.log_level updated. But we only use the value
243 266 # of the file_config if a value was not specified at the command
244 # line.
267 # line, because the command line overrides everything.
245 268 if not hasattr(self.command_line_config.Global, 'log_level'):
246 269 try:
247 270 self.log_level = self.file_config.Global.log_level
248 271 except AttributeError:
249 272 pass # Use existing value
250 273
251 274 def post_load_file_config(self):
252 275 """Do actions after the config file is loaded."""
253 276 pass
254 277
278 def log_file_config(self):
279 if hasattr(self.file_config.Global, 'config_file'):
280 self.log.debug("Config file loaded: <%s>" % self.file_config.Global.config_file)
281 self.log.debug(repr(self.file_config))
282
255 283 def merge_configs(self):
256 284 """Merge the default, command line and file config objects."""
257 285 config = Config()
258 286 config._merge(self.default_config)
259 287 config._merge(self.file_config)
260 288 config._merge(self.command_line_config)
261 289 self.master_config = config
290
291 def log_master_config(self):
262 292 self.log.debug("Master config created:")
263 293 self.log.debug(repr(self.master_config))
264 294
265 295 def pre_construct(self):
266 296 """Do actions after the config has been built, but before construct."""
267 297 pass
268 298
269 299 def construct(self):
270 300 """Construct the main components that make up this app."""
271 301 self.log.debug("Constructing components for application")
272 302
273 303 def post_construct(self):
274 304 """Do actions after construct, but before starting the app."""
275 305 pass
276 306
277 307 def start_app(self):
278 308 """Actually start the app."""
279 309 self.log.debug("Starting application")
280 310
281 311 #-------------------------------------------------------------------------
282 312 # Utility methods
283 313 #-------------------------------------------------------------------------
284 314
285 315 def abort(self):
286 316 """Abort the starting of the application."""
287 317 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
288 318 sys.exit(1)
289 319
290 320 def exit(self):
291 321 self.log.critical("Aborting application: %s" % self.name)
292 322 sys.exit(1)
293 323
294 324 def attempt(self, func, action='abort'):
295 325 try:
296 326 func()
297 327 except SystemExit:
298 328 self.exit()
299 329 except:
300 330 if action == 'abort':
301 331 self.abort()
302 332 elif action == 'exit':
303 333 self.exit()
304 334 No newline at end of file
@@ -1,96 +1,96 b''
1 1 # encoding: utf-8
2 2
3 3 """This module contains blocking clients for the controller interfaces.
4 4
5 5 Unlike the clients in `asyncclient.py`, the clients in this module are fully
6 6 blocking. This means that methods on the clients return the actual results
7 7 rather than a deferred to the result. Also, we manage the Twisted reactor
8 8 for you. This is done by running the reactor in a thread.
9 9
10 10 The main classes in this module are:
11 11
12 12 * MultiEngineClient
13 13 * TaskClient
14 14 * Task
15 15 * CompositeError
16 16 """
17 17
18 18 __docformat__ = "restructuredtext en"
19 19
20 20 #-------------------------------------------------------------------------------
21 21 # Copyright (C) 2008 The IPython Development Team
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #-------------------------------------------------------------------------------
26 26
27 27 #-------------------------------------------------------------------------------
28 28 # Imports
29 29 #-------------------------------------------------------------------------------
30 30
31 31 import sys
32 32
33 33 # from IPython.utils import growl
34 34 # growl.start("IPython1 Client")
35 35
36 36
37 37 from twisted.internet import reactor
38 38 from IPython.kernel.clientconnector import ClientConnector
39 39 from IPython.kernel.twistedutil import ReactorInThread
40 40 from IPython.kernel.twistedutil import blockingCallFromThread
41 41
42 42 # These enable various things
43 43 from IPython.kernel import codeutil
44 import IPython.kernel.magic
44 # import IPython.kernel.magic
45 45
46 46 # Other things that the user will need
47 47 from IPython.kernel.task import MapTask, StringTask
48 48 from IPython.kernel.error import CompositeError
49 49
50 50 #-------------------------------------------------------------------------------
51 51 # Code
52 52 #-------------------------------------------------------------------------------
53 53
54 54 _client_tub = ClientConnector()
55 55
56 56
57 57 def get_multiengine_client(furl_or_file=''):
58 58 """Get the blocking MultiEngine client.
59 59
60 60 :Parameters:
61 61 furl_or_file : str
62 62 A furl or a filename containing a furl. If empty, the
63 63 default furl_file will be used
64 64
65 65 :Returns:
66 66 The connected MultiEngineClient instance
67 67 """
68 68 client = blockingCallFromThread(_client_tub.get_multiengine_client,
69 69 furl_or_file)
70 70 return client.adapt_to_blocking_client()
71 71
72 72 def get_task_client(furl_or_file=''):
73 73 """Get the blocking Task client.
74 74
75 75 :Parameters:
76 76 furl_or_file : str
77 77 A furl or a filename containing a furl. If empty, the
78 78 default furl_file will be used
79 79
80 80 :Returns:
81 81 The connected TaskClient instance
82 82 """
83 83 client = blockingCallFromThread(_client_tub.get_task_client,
84 84 furl_or_file)
85 85 return client.adapt_to_blocking_client()
86 86
87 87
88 88 MultiEngineClient = get_multiengine_client
89 89 TaskClient = get_task_client
90 90
91 91
92 92
93 93 # Now we start the reactor in a thread
94 94 rit = ReactorInThread()
95 95 rit.setDaemon(True)
96 96 rit.start() No newline at end of file
@@ -1,209 +1,228 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 Foolscap related utilities.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import os
19 19 import tempfile
20 20
21 21 from twisted.internet import reactor, defer
22 22 from twisted.python import log
23 23
24 24 from foolscap import Tub, UnauthenticatedTub
25 25
26 26 from IPython.config.loader import Config
27 27
28 28 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
29 29
30 30 from IPython.kernel.error import SecurityError
31 31
32 32 from IPython.utils.traitlets import Int, Str, Bool, Instance
33 33 from IPython.utils.importstring import import_item
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Code
37 37 #-----------------------------------------------------------------------------
38 38
39 39
40 40 # We do this so if a user doesn't have OpenSSL installed, it will try to use
41 41 # an UnauthenticatedTub. But, they will still run into problems if they
42 42 # try to use encrypted furls.
43 43 try:
44 44 import OpenSSL
45 45 except:
46 46 Tub = UnauthenticatedTub
47 47 have_crypto = False
48 48 else:
49 49 have_crypto = True
50 50
51 51
52 52 def check_furl_file_security(furl_file, secure):
53 53 """Remove the old furl_file if changing security modes."""
54 54 if os.path.isfile(furl_file):
55 55 f = open(furl_file, 'r')
56 56 oldfurl = f.read().strip()
57 57 f.close()
58 58 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
59 59 os.remove(furl_file)
60 60
61 61
62 62 def is_secure(furl):
63 63 """Is the given FURL secure or not."""
64 64 if is_valid(furl):
65 65 if furl.startswith("pb://"):
66 66 return True
67 67 elif furl.startswith("pbu://"):
68 68 return False
69 69 else:
70 70 raise ValueError("invalid furl: %s" % furl)
71 71
72 72
73 73 def is_valid(furl):
74 74 """Is the str a valid furl or not."""
75 75 if isinstance(furl, str):
76 76 if furl.startswith("pb://") or furl.startswith("pbu://"):
77 77 return True
78 78 else:
79 79 return False
80 80
81 81
82 82 def find_furl(furl_or_file):
83 83 """Find, validate and return a FURL in a string or file."""
84 84 if isinstance(furl_or_file, str):
85 85 if is_valid(furl_or_file):
86 86 return furl_or_file
87 87 if os.path.isfile(furl_or_file):
88 88 furl = open(furl_or_file, 'r').read().strip()
89 89 if is_valid(furl):
90 90 return furl
91 91 raise ValueError("not a furl or a file containing a furl: %s" % furl_or_file)
92 92
93 93
94 94 def get_temp_furlfile(filename):
95 95 """Return a temporary furl file."""
96 96 return tempfile.mktemp(dir=os.path.dirname(filename),
97 97 prefix=os.path.basename(filename))
98 98
99 99
100 100 def make_tub(ip, port, secure, cert_file):
101 101 """Create a listening tub given an ip, port, and cert_file location.
102 102
103 103 Parameters
104 104 ----------
105 105 ip : str
106 106 The ip address or hostname that the tub should listen on.
107 107 Empty means all interfaces.
108 108 port : int
109 109 The port that the tub should listen on. A value of 0 means
110 110 pick a random port
111 111 secure: bool
112 112 Will the connection be secure (in the Foolscap sense).
113 113 cert_file: str
114 114 A filename of a file to be used for theSSL certificate.
115 115
116 116 Returns
117 117 -------
118 118 A tub, listener tuple.
119 119 """
120 120 if secure:
121 121 if have_crypto:
122 122 tub = Tub(certFile=cert_file)
123 123 else:
124 124 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
125 125 "can't run in secure mode. Try running without "
126 126 "security using 'ipcontroller -xy'.")
127 127 else:
128 128 tub = UnauthenticatedTub()
129 129
130 130 # Set the strport based on the ip and port and start listening
131 131 if ip == '':
132 132 strport = "tcp:%i" % port
133 133 else:
134 134 strport = "tcp:%i:interface=%s" % (port, ip)
135 135 listener = tub.listenOn(strport)
136 136
137 137 return tub, listener
138 138
139 139
140 140 class FCServiceFactory(AdaptedConfiguredObjectFactory):
141 141 """This class creates a tub with various services running in it.
142 142
143 143 The basic idea is that :meth:`create` returns a running :class:`Tub`
144 144 instance that has a number of Foolscap references registered in it.
145 145 This class is a subclass of :class:`IPython.core.component.Component`
146 146 so the IPython configuration and component system are used.
147 147
148 148 Attributes
149 149 ----------
150 150 Interfaces : Config
151 151 A Config instance whose values are sub-Config objects having two
152 152 keys: furl_file and interface_chain.
153 153
154 154 The other attributes are the standard ones for Foolscap.
155 155 """
156 156
157 157 ip = Str('', config=True)
158 158 port = Int(0, config=True)
159 159 secure = Bool(True, config=True)
160 160 cert_file = Str('', config=True)
161 161 location = Str('', config=True)
162 reuse_furls = Bool(False, config=True)
162 163 Interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
163 164
165 def __init__(self, config, adaptee):
166 super(FCServiceFactory, self).__init__(config, adaptee)
167 self._check_reuse_furls()
168
164 169 def _ip_changed(self, name, old, new):
165 170 if new == 'localhost' or new == '127.0.0.1':
166 171 self.location = '127.0.0.1'
167 172
173 def _check_reuse_furls(self):
174 if not self.reuse_furls:
175 furl_files = [i.furl_file for i in self.Interfaces.values()]
176 for ff in furl_files:
177 fullfile = self._get_security_file(ff)
178 if os.path.isfile(fullfile):
179 os.remove(fullfile)
180
181 def _get_security_file(self, filename):
182 return os.path.join(self.config.Global.security_dir, filename)
183
168 184 def create(self):
169 185 """Create and return the Foolscap tub with everything running."""
170 186
171 187 self.tub, self.listener = make_tub(
172 self.ip, self.port, self.secure, self.cert_file)
188 self.ip, self.port, self.secure, self._get_security_file(self.cert_file))
189 log.msg("Created a tub and listener [%r]: %r, %r" % (self.__class__, self.tub, self.listener))
190 log.msg("Interfaces to register [%r]: %r" % (self.__class__, self.Interfaces))
173 191 if not self.secure:
174 192 log.msg("WARNING: running with no security: %s" % self.__class__.__name__)
175 193 reactor.callWhenRunning(self.set_location_and_register)
176 194 return self.tub
177 195
178 196 def set_location_and_register(self):
179 197 """Set the location for the tub and return a deferred."""
180 198
181 199 if self.location == '':
182 200 d = self.tub.setLocationAutomatically()
183 201 else:
184 202 d = defer.maybeDeferred(self.tub.setLocation,
185 203 "%s:%i" % (self.location, self.listener.getPortnum()))
186 204 self.adapt_to_interfaces(d)
187 205
188 206 def adapt_to_interfaces(self, d):
189 207 """Run through the interfaces, adapt and register."""
190 208
191 209 for ifname, ifconfig in self.Interfaces.iteritems():
210 ff = self._get_security_file(ifconfig.furl_file)
192 211 log.msg("Adapting %r to interface: %s" % (self.adaptee, ifname))
193 log.msg("Saving furl for interface [%s] to file: %s" % (ifname, ifconfig.furl_file))
194 check_furl_file_security(ifconfig.furl_file, self.secure)
212 log.msg("Saving furl for interface [%s] to file: %s" % (ifname, ff))
213 check_furl_file_security(ff, self.secure)
195 214 adaptee = self.adaptee
196 215 for i in ifconfig.interface_chain:
197 216 adaptee = import_item(i)(adaptee)
198 d.addCallback(self.register, adaptee, furl_file=ifconfig.furl_file)
217 d.addCallback(self.register, adaptee, furl_file=ff)
199 218
200 219 def register(self, empty, ref, furl_file):
201 220 """Register the reference with the FURL file.
202 221
203 222 The FURL file is created and then moved to make sure that when the
204 223 file appears, the buffer has been flushed and the file closed.
205 224 """
206 225 temp_furl_file = get_temp_furlfile(furl_file)
207 226 self.tub.registerReference(ref, furlFile=temp_furl_file)
208 227 os.rename(temp_furl_file, furl_file)
209 228
@@ -1,266 +1,310 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The IPython controller application
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2009 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import copy
19 import logging
19 20 import os
20 21 import sys
21 22
22 23 from twisted.application import service
23 24 from twisted.internet import reactor, defer
24 25 from twisted.python import log
25 26
26 27 from IPython.config.loader import Config, NoConfigDefault
27 28
28 29 from IPython.core.application import Application, IPythonArgParseConfigLoader
29 30 from IPython.core import release
30 31
31 32 from IPython.utils.traitlets import Int, Str, Bool, Instance
32 33 from IPython.utils.importstring import import_item
33 34
34 35 from IPython.kernel import controllerservice
35 36 from IPython.kernel.configobjfactory import (
36 37 ConfiguredObjectFactory,
37 38 AdaptedConfiguredObjectFactory
38 39 )
39 40
40 41 from IPython.kernel.fcutil import FCServiceFactory
41 42
42 43 #-----------------------------------------------------------------------------
43 # Components for creating services
44 # Default interfaces
44 45 #-----------------------------------------------------------------------------
45 46
46 47
47 48 # The default client interfaces for FCClientServiceFactory.Interfaces
48 49 default_client_interfaces = Config()
49 50 default_client_interfaces.Task.interface_chain = [
50 51 'IPython.kernel.task.ITaskController',
51 52 'IPython.kernel.taskfc.IFCTaskController'
52 53 ]
54
53 55 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
54 56 default_client_interfaces.MultiEngine.interface_chain = [
55 57 'IPython.kernel.multiengine.IMultiEngine',
56 58 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
57 59 ]
60
58 61 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
59 62
60 63 # Make this a dict we can pass to Config.__init__ for the default
61 64 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
62 65
63 66
64 67
65 68 # The default engine interfaces for FCEngineServiceFactory.Interfaces
66 69 default_engine_interfaces = Config()
67 70 default_engine_interfaces.Default.interface_chain = [
68 71 'IPython.kernel.enginefc.IFCControllerBase'
69 72 ]
73
70 74 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
71 75
72 76 # Make this a dict we can pass to Config.__init__ for the default
73 77 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
74 78
75 79
80 #-----------------------------------------------------------------------------
81 # Service factories
82 #-----------------------------------------------------------------------------
83
76 84
77 85 class FCClientServiceFactory(FCServiceFactory):
78 86 """A Foolscap implementation of the client services."""
79 87
80 88 cert_file = Str('ipcontroller-client.pem', config=True)
81 89 Interfaces = Instance(klass=Config, kw=default_client_interfaces,
82 90 allow_none=False, config=True)
83 91
84 92
85 93 class FCEngineServiceFactory(FCServiceFactory):
86 94 """A Foolscap implementation of the engine services."""
87 95
88 96 cert_file = Str('ipcontroller-engine.pem', config=True)
89 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
97 Interfaces = Instance(klass=dict, kw=default_engine_interfaces,
90 98 allow_none=False, config=True)
91 99
92 100
93 101 #-----------------------------------------------------------------------------
94 102 # The main application
95 103 #-----------------------------------------------------------------------------
96 104
97 105
98 106 cl_args = (
99 107 # Client config
100 108 (('--client-ip',), dict(
101 109 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
102 110 help='The IP address or hostname the controller will listen on for client connections.',
103 111 metavar='FCClientServiceFactory.ip')
104 112 ),
105 113 (('--client-port',), dict(
106 114 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
107 115 help='The port the controller will listen on for client connections.',
108 116 metavar='FCClientServiceFactory.port')
109 117 ),
110 118 (('--client-location',), dict(
111 119 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
112 120 help='The hostname or ip that clients should connect to.',
113 121 metavar='FCClientServiceFactory.location')
114 122 ),
115 123 (('-x',), dict(
116 124 action='store_false', dest='FCClientServiceFactory.secure', default=NoConfigDefault,
117 125 help='Turn off all client security.')
118 126 ),
119 (('--client-cert-file',), dict(
120 type=str, dest='FCClientServiceFactory.cert_file', default=NoConfigDefault,
121 help='File to store the client SSL certificate in.',
122 metavar='FCClientServiceFactory.cert_file')
123 ),
124 (('--task-furl-file',), dict(
125 type=str, dest='FCClientServiceFactory.Interfaces.Task.furl_file', default=NoConfigDefault,
126 help='File to store the FURL in for task clients to connect with.',
127 metavar='FCClientServiceFactory.Interfaces.Task.furl_file')
128 ),
129 (('--multiengine-furl-file',), dict(
130 type=str, dest='FCClientServiceFactory.Interfaces.MultiEngine.furl_file', default=NoConfigDefault,
131 help='File to store the FURL in for multiengine clients to connect with.',
132 metavar='FCClientServiceFactory.Interfaces.MultiEngine.furl_file')
133 ),
134 127 # Engine config
135 128 (('--engine-ip',), dict(
136 129 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
137 130 help='The IP address or hostname the controller will listen on for engine connections.',
138 131 metavar='FCEngineServiceFactory.ip')
139 132 ),
140 133 (('--engine-port',), dict(
141 134 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
142 135 help='The port the controller will listen on for engine connections.',
143 136 metavar='FCEngineServiceFactory.port')
144 137 ),
145 138 (('--engine-location',), dict(
146 139 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
147 140 help='The hostname or ip that engines should connect to.',
148 141 metavar='FCEngineServiceFactory.location')
149 142 ),
150 143 (('-y',), dict(
151 144 action='store_false', dest='FCEngineServiceFactory.secure', default=NoConfigDefault,
152 145 help='Turn off all engine security.')
153 146 ),
154 (('--engine-cert-file',), dict(
155 type=str, dest='FCEngineServiceFactory.cert_file', default=NoConfigDefault,
156 help='File to store the client SSL certificate in.',
157 metavar='FCEngineServiceFactory.cert_file')
158 ),
159 (('--engine-furl-file',), dict(
160 type=str, dest='FCEngineServiceFactory.Interfaces.Default.furl_file', default=NoConfigDefault,
161 help='File to store the FURL in for engines to connect with.',
162 metavar='FCEngineServiceFactory.Interfaces.Default.furl_file')
163 ),
164 147 # Global config
165 (('-l','--logfile'), dict(
166 type=str, dest='Global.logfile', default=NoConfigDefault,
167 help='Log file name (default is stdout)',
168 metavar='Global.logfile')
148 (('--log-to-file',), dict(
149 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
150 help='Log to a file in the log directory (default is stdout)')
169 151 ),
170 (('-r',), dict(
152 (('-r','--reuse-furls'), dict(
171 153 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
172 154 help='Try to reuse all FURL files.')
173 )
155 ),
156 (('-cluster_dir', '--cluster-dir',), dict(
157 type=str, dest='Global.cluster_dir', default=NoConfigDefault,
158 help='Absolute or relative path to the cluster directory.',
159 metavar='Global.cluster_dir')
160 ),
174 161 )
175 162
176 163
177 164 class IPControllerAppCLConfigLoader(IPythonArgParseConfigLoader):
178 165
179 166 arguments = cl_args
180 167
181 168
182 169 _default_config_file_name = 'ipcontroller_config.py'
183 170
184 171 class IPControllerApp(Application):
185 172
186 173 name = 'ipcontroller'
187 174 config_file_name = _default_config_file_name
175 default_log_level = logging.DEBUG
188 176
189 177 def create_default_config(self):
190 178 super(IPControllerApp, self).create_default_config()
191 self.default_config.Global.logfile = ''
192 179 self.default_config.Global.reuse_furls = False
193 180 self.default_config.Global.import_statements = []
181 self.default_config.Global.profile = 'default'
182 self.default_config.Global.log_dir_name = 'log'
183 self.default_config.Global.security_dir_name = 'security'
184 self.default_config.Global.log_to_file = False
185 # Resolve the default cluster_dir using the default profile
186 self.default_config.Global.cluster_dir = ''
194 187
195 188 def create_command_line_config(self):
196 189 """Create and return a command line config loader."""
197 190
198 191 return IPControllerAppCLConfigLoader(
199 192 description="Start an IPython controller",
200 193 version=release.version)
201 194
195 def find_config_file_name(self):
196 """Find the config file name for this application."""
197 self.find_cluster_dir()
198 self.create_cluster_dir()
199
200 def find_cluster_dir(self):
201 """This resolves into full paths, the various cluster directories.
202
203 This method must set ``self.cluster_dir`` to the full paths of
204 the directory.
205 """
206 # Ignore self.command_line_config.Global.config_file
207 # Instead, first look for an explicit cluster_dir
208 try:
209 self.cluster_dir = self.command_line_config.Global.cluster_dir
210 except AttributeError:
211 self.cluster_dir = self.default_config.Global.cluster_dir
212 self.cluster_dir = os.path.expandvars(os.path.expanduser(self.cluster_dir))
213 if not self.cluster_dir:
214 # Then look for a profile
215 try:
216 self.profile = self.command_line_config.Global.profile
217 except AttributeError:
218 self.profile = self.default_config.Global.profile
219 cluster_dir_name = 'cluster_' + self.profile
220 try_this = os.path.join(os.getcwd(), cluster_dir_name)
221 if os.path.isdir(try_this):
222 self.cluster_dir = try_this
223 else:
224 self.cluster_dir = os.path.join(self.ipythondir, cluster_dir_name)
225 # These have to be set because they could be different from the one
226 # that we just computed. Because command line has the highest
227 # priority, this will always end up in the master_config.
228 self.default_config.Global.cluster_dir = self.cluster_dir
229 self.command_line_config.Global.cluster_dir = self.cluster_dir
230
231 def create_cluster_dir(self):
232 """Make sure that the cluster, security and log dirs exist."""
233 if not os.path.isdir(self.cluster_dir):
234 os.makedirs(self.cluster_dir, mode=0777)
235
236 def find_config_file_paths(self):
237 """Set the search paths for resolving the config file."""
238 self.config_file_paths = (self.cluster_dir,)
239
240 def pre_construct(self):
241 # Now set the security_dir and log_dir and create them. We use
242 # the names an construct the absolute paths.
243 security_dir = os.path.join(self.master_config.Global.cluster_dir,
244 self.master_config.Global.security_dir_name)
245 log_dir = os.path.join(self.master_config.Global.cluster_dir,
246 self.master_config.Global.log_dir_name)
247 if not os.path.isdir(security_dir):
248 os.mkdir(security_dir, 0700)
249 else:
250 os.chmod(security_dir, 0700)
251 if not os.path.isdir(log_dir):
252 os.mkdir(log_dir, 0777)
253
254 self.security_dir = self.master_config.Global.security_dir = security_dir
255 self.log_dir = self.master_config.Global.log_dir = log_dir
256
257 # Now setup reuse_furls
258 if hasattr(self.master_config.Global.reuse_furls):
259 self.master_config.FCClientServiceFactory.reuse_furls = \
260 self.master_config.Global.reuse_furls
261 self.master_config.FCEngineServiceFactory.reuse_furls = \
262 self.master_config.Global.reuse_furls
263
202 264 def construct(self):
203 265 # I am a little hesitant to put these into InteractiveShell itself.
204 266 # But that might be the place for them
205 267 sys.path.insert(0, '')
206 268
207 269 self.start_logging()
208 270 self.import_statements()
209 self.reuse_furls()
210 271
211 272 # Create the service hierarchy
212 273 self.main_service = service.MultiService()
213 274 # The controller service
214 275 controller_service = controllerservice.ControllerService()
215 276 controller_service.setServiceParent(self.main_service)
216 277 # The client tub and all its refereceables
217 278 csfactory = FCClientServiceFactory(self.master_config, controller_service)
218 279 client_service = csfactory.create()
219 280 client_service.setServiceParent(self.main_service)
220 281 # The engine tub
221 282 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
222 283 engine_service = esfactory.create()
223 284 engine_service.setServiceParent(self.main_service)
224 285
225 286 def start_logging(self):
226 logfile = self.master_config.Global.logfile
227 if logfile:
228 logfile = logfile + str(os.getpid()) + '.log'
229 try:
230 openLogFile = open(logfile, 'w')
231 except:
232 openLogFile = sys.stdout
287 if self.master_config.Global.log_to_file:
288 log_filename = self.name + '-' + str(os.getpid()) + '.log'
289 logfile = os.path.join(self.log_dir, log_filename)
290 open_log_file = open(logfile, 'w')
233 291 else:
234 openLogFile = sys.stdout
235 log.startLogging(openLogFile)
292 open_log_file = sys.stdout
293 log.startLogging(open_log_file)
236 294
237 295 def import_statements(self):
238 296 statements = self.master_config.Global.import_statements
239 297 for s in statements:
240 298 try:
241 299 exec s in globals(), locals()
242 300 except:
243 301 log.msg("Error running import statement: %s" % s)
244 302
245 def reuse_furls(self):
246 # This logic might need to be moved into the components
247 # Delete old furl files unless the reuse_furls is set
248 reuse = self.master_config.Global.reuse_furls
249 # if not reuse:
250 # paths = (
251 # self.master_config.FCEngineServiceFactory.Interfaces.Default.furl_file,
252 # self.master_config.FCClientServiceFactory.Interfaces.Task.furl_file,
253 # self.master_config.FCClientServiceFactory.Interfaces.MultiEngine.furl_file
254 # )
255 # for p in paths:
256 # if os.path.isfile(p):
257 # os.remove(p)
258
259 303 def start_app(self):
260 304 # Start the controller service and set things running
261 305 self.main_service.startService()
262 306 reactor.run()
263 307
264 308 if __name__ == '__main__':
265 309 app = IPControllerApp()
266 310 app.start()
General Comments 0
You need to be logged in to leave comments. Login now