##// END OF EJS Templates
Fixing small bugs in ipcluster stuff....
Brian Granger -
Show More
@@ -1,79 +1,79 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A class for creating a Twisted service that is configured using IPython's
4 A class for creating a Twisted service that is configured using IPython's
5 configuration system.
5 configuration system.
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2009 The IPython Development Team
9 # Copyright (C) 2008-2009 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 zope.interface as zi
19 import zope.interface as zi
20
20
21 from IPython.config.configurable import Configurable
21 from IPython.config.configurable import Configurable
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class IConfiguredObjectFactory(zi.Interface):
28 class IConfiguredObjectFactory(zi.Interface):
29 """I am a component that creates a configured object.
29 """I am a component that creates a configured object.
30
30
31 This class is useful if you want to configure a class that is not a
31 This class is useful if you want to configure a class that is not a
32 subclass of :class:`IPython.config.configurable.Configurable`.
32 subclass of :class:`IPython.config.configurable.Configurable`.
33 """
33 """
34
34
35 def __init__(config):
35 def __init__(config=None):
36 """Get ready to configure the object using config."""
36 """Get ready to configure the object using config."""
37
37
38 def create():
38 def create():
39 """Return an instance of the configured object."""
39 """Return an instance of the configured object."""
40
40
41
41
42 class ConfiguredObjectFactory(Configurable):
42 class ConfiguredObjectFactory(Configurable):
43
43
44 zi.implements(IConfiguredObjectFactory)
44 zi.implements(IConfiguredObjectFactory)
45
45
46 def __init__(self, config=None):
46 def __init__(self, config=None):
47 super(ConfiguredObjectFactory, self).__init__(config=config)
47 super(ConfiguredObjectFactory, self).__init__(config=config)
48
48
49 def create(self):
49 def create(self):
50 raise NotImplementedError('create must be implemented in a subclass')
50 raise NotImplementedError('create must be implemented in a subclass')
51
51
52
52
53 class IAdaptedConfiguredObjectFactory(zi.Interface):
53 class IAdaptedConfiguredObjectFactory(zi.Interface):
54 """I am a component that adapts and configures an object.
54 """I am a component that adapts and configures an object.
55
55
56 This class is useful if you have the adapt an instance and configure it.
56 This class is useful if you have the adapt an instance and configure it.
57 """
57 """
58
58
59 def __init__(config=None, adaptee=None):
59 def __init__(config=None, adaptee=None):
60 """Get ready to adapt adaptee and then configure it using config."""
60 """Get ready to adapt adaptee and then configure it using config."""
61
61
62 def create():
62 def create():
63 """Return an instance of the adapted and configured object."""
63 """Return an instance of the adapted and configured object."""
64
64
65
65
66 class AdaptedConfiguredObjectFactory(Configurable):
66 class AdaptedConfiguredObjectFactory(Configurable):
67
67
68 # zi.implements(IAdaptedConfiguredObjectFactory)
68 # zi.implements(IAdaptedConfiguredObjectFactory)
69
69
70 def __init__(self, config=None, adaptee=None):
70 def __init__(self, config=None, adaptee=None):
71 # print
71 # print
72 # print "config pre:", config
72 # print "config pre:", config
73 super(AdaptedConfiguredObjectFactory, self).__init__(config=config)
73 super(AdaptedConfiguredObjectFactory, self).__init__(config=config)
74 # print
74 # print
75 # print "config post:", config
75 # print "config post:", config
76 self.adaptee = adaptee
76 self.adaptee = adaptee
77
77
78 def create(self):
78 def create(self):
79 raise NotImplementedError('create must be implemented in a subclass')
79 raise NotImplementedError('create must be implemented in a subclass')
@@ -1,296 +1,296 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Foolscap related utilities.
4 Foolscap related utilities.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import os
20 import os
21 import tempfile
21 import tempfile
22
22
23 from twisted.internet import reactor, defer
23 from twisted.internet import reactor, defer
24 from twisted.python import log
24 from twisted.python import log
25
25
26 import foolscap
26 import foolscap
27 try:
27 try:
28 from foolscap.api import Tub, UnauthenticatedTub
28 from foolscap.api import Tub, UnauthenticatedTub
29 except ImportError:
29 except ImportError:
30 from foolscap import Tub, UnauthenticatedTub
30 from foolscap import Tub, UnauthenticatedTub
31
31
32 from IPython.config.loader import Config
32 from IPython.config.loader import Config
33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
33 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
34 from IPython.kernel.error import SecurityError
34 from IPython.kernel.error import SecurityError
35
35
36 from IPython.utils.importstring import import_item
36 from IPython.utils.importstring import import_item
37 from IPython.utils.path import expand_path
37 from IPython.utils.path import expand_path
38 from IPython.utils.traitlets import Int, Str, Bool, Instance
38 from IPython.utils.traitlets import Int, Str, Bool, Instance
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Code
41 # Code
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44
44
45 # We do this so if a user doesn't have OpenSSL installed, it will try to use
45 # We do this so if a user doesn't have OpenSSL installed, it will try to use
46 # an UnauthenticatedTub. But, they will still run into problems if they
46 # an UnauthenticatedTub. But, they will still run into problems if they
47 # try to use encrypted furls.
47 # try to use encrypted furls.
48 try:
48 try:
49 import OpenSSL
49 import OpenSSL
50 except:
50 except:
51 Tub = UnauthenticatedTub
51 Tub = UnauthenticatedTub
52 have_crypto = False
52 have_crypto = False
53 else:
53 else:
54 have_crypto = True
54 have_crypto = True
55
55
56
56
57 class FURLError(Exception):
57 class FURLError(Exception):
58 pass
58 pass
59
59
60
60
61 def check_furl_file_security(furl_file, secure):
61 def check_furl_file_security(furl_file, secure):
62 """Remove the old furl_file if changing security modes."""
62 """Remove the old furl_file if changing security modes."""
63 furl_file = expand_path(furl_file)
63 furl_file = expand_path(furl_file)
64 if os.path.isfile(furl_file):
64 if os.path.isfile(furl_file):
65 with open(furl_file, 'r') as f:
65 with open(furl_file, 'r') as f:
66 oldfurl = f.read().strip()
66 oldfurl = f.read().strip()
67 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
67 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
68 os.remove(furl_file)
68 os.remove(furl_file)
69
69
70
70
71 def is_secure(furl):
71 def is_secure(furl):
72 """Is the given FURL secure or not."""
72 """Is the given FURL secure or not."""
73 if is_valid_furl(furl):
73 if is_valid_furl(furl):
74 if furl.startswith("pb://"):
74 if furl.startswith("pb://"):
75 return True
75 return True
76 elif furl.startswith("pbu://"):
76 elif furl.startswith("pbu://"):
77 return False
77 return False
78 else:
78 else:
79 raise FURLError("invalid FURL: %s" % furl)
79 raise FURLError("invalid FURL: %s" % furl)
80
80
81
81
82 def is_valid_furl(furl):
82 def is_valid_furl(furl):
83 """Is the str a valid FURL or not."""
83 """Is the str a valid FURL or not."""
84 if isinstance(furl, str):
84 if isinstance(furl, str):
85 if furl.startswith("pb://") or furl.startswith("pbu://"):
85 if furl.startswith("pb://") or furl.startswith("pbu://"):
86 return True
86 return True
87 else:
87 else:
88 return False
88 return False
89 else:
89 else:
90 return False
90 return False
91
91
92
92
93 def is_valid_furl_file(furl_or_file):
93 def is_valid_furl_file(furl_or_file):
94 """See if furl_or_file exists and contains a valid FURL.
94 """See if furl_or_file exists and contains a valid FURL.
95
95
96 This doesn't try to read the contents because often we have to validate
96 This doesn't try to read the contents because often we have to validate
97 FURL files that are created, but don't yet have a FURL written to them.
97 FURL files that are created, but don't yet have a FURL written to them.
98 """
98 """
99 if isinstance(furl_or_file, (str, unicode)):
99 if isinstance(furl_or_file, (str, unicode)):
100 path, furl_filename = os.path.split(furl_or_file)
100 path, furl_filename = os.path.split(furl_or_file)
101 if os.path.isdir(path) and furl_filename.endswith('.furl'):
101 if os.path.isdir(path) and furl_filename.endswith('.furl'):
102 return True
102 return True
103 return False
103 return False
104
104
105
105
106 def find_furl(furl_or_file):
106 def find_furl(furl_or_file):
107 """Find, validate and return a FURL in a string or file.
107 """Find, validate and return a FURL in a string or file.
108
108
109 This calls :func:`IPython.utils.path.expand_path` on the argument to
109 This calls :func:`IPython.utils.path.expand_path` on the argument to
110 properly handle ``~`` and ``$`` variables in the path.
110 properly handle ``~`` and ``$`` variables in the path.
111 """
111 """
112 if is_valid_furl(furl_or_file):
112 if is_valid_furl(furl_or_file):
113 return furl_or_file
113 return furl_or_file
114 furl_or_file = expand_path(furl_or_file)
114 furl_or_file = expand_path(furl_or_file)
115 if is_valid_furl_file(furl_or_file):
115 if is_valid_furl_file(furl_or_file):
116 with open(furl_or_file, 'r') as f:
116 with open(furl_or_file, 'r') as f:
117 furl = f.read().strip()
117 furl = f.read().strip()
118 if is_valid_furl(furl):
118 if is_valid_furl(furl):
119 return furl
119 return furl
120 raise FURLError("Not a valid FURL or FURL file: %r" % furl_or_file)
120 raise FURLError("Not a valid FURL or FURL file: %r" % furl_or_file)
121
121
122
122
123 def is_valid_furl_or_file(furl_or_file):
123 def is_valid_furl_or_file(furl_or_file):
124 """Validate a FURL or a FURL file.
124 """Validate a FURL or a FURL file.
125
125
126 If ``furl_or_file`` looks like a file, we simply make sure its directory
126 If ``furl_or_file`` looks like a file, we simply make sure its directory
127 exists and that it has a ``.furl`` file extension. We don't try to see
127 exists and that it has a ``.furl`` file extension. We don't try to see
128 if the FURL file exists or to read its contents. This is useful for
128 if the FURL file exists or to read its contents. This is useful for
129 cases where auto re-connection is being used.
129 cases where auto re-connection is being used.
130 """
130 """
131 if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file):
131 if is_valid_furl(furl_or_file) or is_valid_furl_file(furl_or_file):
132 return True
132 return True
133 else:
133 else:
134 return False
134 return False
135
135
136
136
137 def validate_furl_or_file(furl_or_file):
137 def validate_furl_or_file(furl_or_file):
138 """Like :func:`is_valid_furl_or_file`, but raises an error."""
138 """Like :func:`is_valid_furl_or_file`, but raises an error."""
139 if not is_valid_furl_or_file(furl_or_file):
139 if not is_valid_furl_or_file(furl_or_file):
140 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
140 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
141
141
142
142
143 def get_temp_furlfile(filename):
143 def get_temp_furlfile(filename):
144 """Return a temporary FURL file."""
144 """Return a temporary FURL file."""
145 return tempfile.mktemp(dir=os.path.dirname(filename),
145 return tempfile.mktemp(dir=os.path.dirname(filename),
146 prefix=os.path.basename(filename))
146 prefix=os.path.basename(filename))
147
147
148
148
149 def make_tub(ip, port, secure, cert_file):
149 def make_tub(ip, port, secure, cert_file):
150 """Create a listening tub given an ip, port, and cert_file location.
150 """Create a listening tub given an ip, port, and cert_file location.
151
151
152 Parameters
152 Parameters
153 ----------
153 ----------
154 ip : str
154 ip : str
155 The ip address or hostname that the tub should listen on.
155 The ip address or hostname that the tub should listen on.
156 Empty means all interfaces.
156 Empty means all interfaces.
157 port : int
157 port : int
158 The port that the tub should listen on. A value of 0 means
158 The port that the tub should listen on. A value of 0 means
159 pick a random port
159 pick a random port
160 secure: bool
160 secure: bool
161 Will the connection be secure (in the Foolscap sense).
161 Will the connection be secure (in the Foolscap sense).
162 cert_file: str
162 cert_file: str
163 A filename of a file to be used for theSSL certificate.
163 A filename of a file to be used for theSSL certificate.
164
164
165 Returns
165 Returns
166 -------
166 -------
167 A tub, listener tuple.
167 A tub, listener tuple.
168 """
168 """
169 if secure:
169 if secure:
170 if have_crypto:
170 if have_crypto:
171 tub = Tub(certFile=cert_file)
171 tub = Tub(certFile=cert_file)
172 else:
172 else:
173 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
173 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
174 "can't run in secure mode. Try running without "
174 "can't run in secure mode. Try running without "
175 "security using 'ipcontroller -xy'.")
175 "security using 'ipcontroller -xy'.")
176 else:
176 else:
177 tub = UnauthenticatedTub()
177 tub = UnauthenticatedTub()
178
178
179 # Set the strport based on the ip and port and start listening
179 # Set the strport based on the ip and port and start listening
180 if ip == '':
180 if ip == '':
181 strport = "tcp:%i" % port
181 strport = "tcp:%i" % port
182 else:
182 else:
183 strport = "tcp:%i:interface=%s" % (port, ip)
183 strport = "tcp:%i:interface=%s" % (port, ip)
184 log.msg("Starting listener with [secure=%r] on: %s" % (secure, strport))
184 log.msg("Starting listener with [secure=%r] on: %s" % (secure, strport))
185 listener = tub.listenOn(strport)
185 listener = tub.listenOn(strport)
186
186
187 return tub, listener
187 return tub, listener
188
188
189
189
190 class FCServiceFactory(AdaptedConfiguredObjectFactory):
190 class FCServiceFactory(AdaptedConfiguredObjectFactory):
191 """This class creates a tub with various services running in it.
191 """This class creates a tub with various services running in it.
192
192
193 The basic idea is that :meth:`create` returns a running :class:`Tub`
193 The basic idea is that :meth:`create` returns a running :class:`Tub`
194 instance that has a number of Foolscap references registered in it. This
194 instance that has a number of Foolscap references registered in it. This
195 class is a subclass of :class:`IPython.config.configurable.Configurable`
195 class is a subclass of :class:`IPython.config.configurable.Configurable`
196 so the IPython configuration system is used.
196 so the IPython configuration system is used.
197
197
198 Attributes
198 Attributes
199 ----------
199 ----------
200 interfaces : Config
200 interfaces : Config
201 A Config instance whose values are sub-Config objects having two
201 A Config instance whose values are sub-Config objects having two
202 keys: furl_file and interface_chain.
202 keys: furl_file and interface_chain.
203
203
204 The other attributes are the standard ones for Foolscap.
204 The other attributes are the standard ones for Foolscap.
205 """
205 """
206
206
207 ip = Str('', config=True)
207 ip = Str('', config=True)
208 port = Int(0, config=True)
208 port = Int(0, config=True)
209 secure = Bool(True, config=True)
209 secure = Bool(True, config=True)
210 cert_file = Str('', config=True)
210 cert_file = Str('', config=True)
211 location = Str('', config=True)
211 location = Str('', config=True)
212 reuse_furls = Bool(False, config=True)
212 reuse_furls = Bool(False, config=True)
213 interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
213 interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
214
214
215 def __init__(self, config, adaptee):
215 def __init__(self, config=None, adaptee=None):
216 super(FCServiceFactory, self).__init__(config, adaptee)
216 super(FCServiceFactory, self).__init__(config=config, adaptee=adaptee)
217 self._check_reuse_furls()
217 self._check_reuse_furls()
218
218
219 def _ip_changed(self, name, old, new):
219 def _ip_changed(self, name, old, new):
220 if new == 'localhost' or new == '127.0.0.1':
220 if new == 'localhost' or new == '127.0.0.1':
221 self.location = '127.0.0.1'
221 self.location = '127.0.0.1'
222
222
223 def _check_reuse_furls(self):
223 def _check_reuse_furls(self):
224 furl_files = [i.furl_file for i in self.interfaces.values()]
224 furl_files = [i.furl_file for i in self.interfaces.values()]
225 for ff in furl_files:
225 for ff in furl_files:
226 fullfile = self._get_security_file(ff)
226 fullfile = self._get_security_file(ff)
227 if self.reuse_furls:
227 if self.reuse_furls:
228 if self.port==0:
228 if self.port==0:
229 raise FURLError("You are trying to reuse the FURL file "
229 raise FURLError("You are trying to reuse the FURL file "
230 "for this connection, but the port for this connection "
230 "for this connection, but the port for this connection "
231 "is set to 0 (autoselect). To reuse the FURL file "
231 "is set to 0 (autoselect). To reuse the FURL file "
232 "you need to specify specific port to listen on."
232 "you need to specify specific port to listen on."
233 )
233 )
234 else:
234 else:
235 log.msg("Reusing FURL file: %s" % fullfile)
235 log.msg("Reusing FURL file: %s" % fullfile)
236 else:
236 else:
237 if os.path.isfile(fullfile):
237 if os.path.isfile(fullfile):
238 log.msg("Removing old FURL file: %s" % fullfile)
238 log.msg("Removing old FURL file: %s" % fullfile)
239 os.remove(fullfile)
239 os.remove(fullfile)
240
240
241 def _get_security_file(self, filename):
241 def _get_security_file(self, filename):
242 return os.path.join(self.config.Global.security_dir, filename)
242 return os.path.join(self.config.Global.security_dir, filename)
243
243
244 def create(self):
244 def create(self):
245 """Create and return the Foolscap tub with everything running."""
245 """Create and return the Foolscap tub with everything running."""
246
246
247 self.tub, self.listener = make_tub(
247 self.tub, self.listener = make_tub(
248 self.ip, self.port, self.secure,
248 self.ip, self.port, self.secure,
249 self._get_security_file(self.cert_file)
249 self._get_security_file(self.cert_file)
250 )
250 )
251 # log.msg("Interfaces to register [%r]: %r" % \
251 # log.msg("Interfaces to register [%r]: %r" % \
252 # (self.__class__, self.interfaces))
252 # (self.__class__, self.interfaces))
253 if not self.secure:
253 if not self.secure:
254 log.msg("WARNING: running with no security: %s" % \
254 log.msg("WARNING: running with no security: %s" % \
255 self.__class__.__name__)
255 self.__class__.__name__)
256 reactor.callWhenRunning(self.set_location_and_register)
256 reactor.callWhenRunning(self.set_location_and_register)
257 return self.tub
257 return self.tub
258
258
259 def set_location_and_register(self):
259 def set_location_and_register(self):
260 """Set the location for the tub and return a deferred."""
260 """Set the location for the tub and return a deferred."""
261
261
262 if self.location == '':
262 if self.location == '':
263 d = self.tub.setLocationAutomatically()
263 d = self.tub.setLocationAutomatically()
264 else:
264 else:
265 d = defer.maybeDeferred(self.tub.setLocation,
265 d = defer.maybeDeferred(self.tub.setLocation,
266 "%s:%i" % (self.location, self.listener.getPortnum()))
266 "%s:%i" % (self.location, self.listener.getPortnum()))
267 self.adapt_to_interfaces(d)
267 self.adapt_to_interfaces(d)
268
268
269 def adapt_to_interfaces(self, d):
269 def adapt_to_interfaces(self, d):
270 """Run through the interfaces, adapt and register."""
270 """Run through the interfaces, adapt and register."""
271
271
272 for ifname, ifconfig in self.interfaces.iteritems():
272 for ifname, ifconfig in self.interfaces.iteritems():
273 ff = self._get_security_file(ifconfig.furl_file)
273 ff = self._get_security_file(ifconfig.furl_file)
274 log.msg("Adapting [%s] to interface: %s" % \
274 log.msg("Adapting [%s] to interface: %s" % \
275 (self.adaptee.__class__.__name__, ifname))
275 (self.adaptee.__class__.__name__, ifname))
276 log.msg("Saving FURL for interface [%s] to file: %s" % (ifname, ff))
276 log.msg("Saving FURL for interface [%s] to file: %s" % (ifname, ff))
277 check_furl_file_security(ff, self.secure)
277 check_furl_file_security(ff, self.secure)
278 adaptee = self.adaptee
278 adaptee = self.adaptee
279 for i in ifconfig.interface_chain:
279 for i in ifconfig.interface_chain:
280 adaptee = import_item(i)(adaptee)
280 adaptee = import_item(i)(adaptee)
281 d.addCallback(self.register, adaptee, furl_file=ff)
281 d.addCallback(self.register, adaptee, furl_file=ff)
282
282
283 def register(self, empty, ref, furl_file):
283 def register(self, empty, ref, furl_file):
284 """Register the reference with the FURL file.
284 """Register the reference with the FURL file.
285
285
286 The FURL file is created and then moved to make sure that when the
286 The FURL file is created and then moved to make sure that when the
287 file appears, the buffer has been flushed and the file closed. This
287 file appears, the buffer has been flushed and the file closed. This
288 is not done if we are re-using FURLS however.
288 is not done if we are re-using FURLS however.
289 """
289 """
290 if self.reuse_furls:
290 if self.reuse_furls:
291 self.tub.registerReference(ref, furlFile=furl_file)
291 self.tub.registerReference(ref, furlFile=furl_file)
292 else:
292 else:
293 temp_furl_file = get_temp_furlfile(furl_file)
293 temp_furl_file = get_temp_furlfile(furl_file)
294 self.tub.registerReference(ref, furlFile=temp_furl_file)
294 self.tub.registerReference(ref, furlFile=temp_furl_file)
295 os.rename(temp_furl_file, furl_file)
295 os.rename(temp_furl_file, furl_file)
296
296
@@ -1,271 +1,271 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython controller application.
4 The IPython controller application.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import copy
20 import copy
21 import sys
21 import sys
22
22
23 from twisted.application import service
23 from twisted.application import service
24 from twisted.internet import reactor
24 from twisted.internet import reactor
25 from twisted.python import log
25 from twisted.python import log
26
26
27 from IPython.config.loader import Config
27 from IPython.config.loader import Config
28 from IPython.kernel import controllerservice
28 from IPython.kernel import controllerservice
29 from IPython.kernel.clusterdir import (
29 from IPython.kernel.clusterdir import (
30 ApplicationWithClusterDir,
30 ApplicationWithClusterDir,
31 ClusterDirConfigLoader
31 ClusterDirConfigLoader
32 )
32 )
33 from IPython.kernel.fcutil import FCServiceFactory, FURLError
33 from IPython.kernel.fcutil import FCServiceFactory, FURLError
34 from IPython.utils.traitlets import Instance, Unicode
34 from IPython.utils.traitlets import Instance, Unicode
35
35
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # Module level variables
38 # Module level variables
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41
41
42 #: The default config file name for this application
42 #: The default config file name for this application
43 default_config_file_name = u'ipcontroller_config.py'
43 default_config_file_name = u'ipcontroller_config.py'
44
44
45
45
46 _description = """Start the IPython controller for parallel computing.
46 _description = """Start the IPython controller for parallel computing.
47
47
48 The IPython controller provides a gateway between the IPython engines and
48 The IPython controller provides a gateway between the IPython engines and
49 clients. The controller needs to be started before the engines and can be
49 clients. The controller needs to be started before the engines and can be
50 configured using command line options or using a cluster directory. Cluster
50 configured using command line options or using a cluster directory. Cluster
51 directories contain config, log and security files and are usually located in
51 directories contain config, log and security files and are usually located in
52 your .ipython directory and named as "cluster_<profile>". See the --profile
52 your .ipython directory and named as "cluster_<profile>". See the --profile
53 and --cluster-dir options for details.
53 and --cluster-dir options for details.
54 """
54 """
55
55
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57 # Default interfaces
57 # Default interfaces
58 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
59
59
60 # The default client interfaces for FCClientServiceFactory.interfaces
60 # The default client interfaces for FCClientServiceFactory.interfaces
61 default_client_interfaces = Config()
61 default_client_interfaces = Config()
62 default_client_interfaces.Task.interface_chain = [
62 default_client_interfaces.Task.interface_chain = [
63 'IPython.kernel.task.ITaskController',
63 'IPython.kernel.task.ITaskController',
64 'IPython.kernel.taskfc.IFCTaskController'
64 'IPython.kernel.taskfc.IFCTaskController'
65 ]
65 ]
66
66
67 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
67 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
68
68
69 default_client_interfaces.MultiEngine.interface_chain = [
69 default_client_interfaces.MultiEngine.interface_chain = [
70 'IPython.kernel.multiengine.IMultiEngine',
70 'IPython.kernel.multiengine.IMultiEngine',
71 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
71 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
72 ]
72 ]
73
73
74 default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
74 default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
75
75
76 # Make this a dict we can pass to Config.__init__ for the default
76 # Make this a dict we can pass to Config.__init__ for the default
77 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
77 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
78
78
79
79
80
80
81 # The default engine interfaces for FCEngineServiceFactory.interfaces
81 # The default engine interfaces for FCEngineServiceFactory.interfaces
82 default_engine_interfaces = Config()
82 default_engine_interfaces = Config()
83 default_engine_interfaces.Default.interface_chain = [
83 default_engine_interfaces.Default.interface_chain = [
84 'IPython.kernel.enginefc.IFCControllerBase'
84 'IPython.kernel.enginefc.IFCControllerBase'
85 ]
85 ]
86
86
87 default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
87 default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
88
88
89 # Make this a dict we can pass to Config.__init__ for the default
89 # Make this a dict we can pass to Config.__init__ for the default
90 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
90 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
91
91
92
92
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94 # Service factories
94 # Service factories
95 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
96
96
97
97
98 class FCClientServiceFactory(FCServiceFactory):
98 class FCClientServiceFactory(FCServiceFactory):
99 """A Foolscap implementation of the client services."""
99 """A Foolscap implementation of the client services."""
100
100
101 cert_file = Unicode(u'ipcontroller-client.pem', config=True)
101 cert_file = Unicode(u'ipcontroller-client.pem', config=True)
102 interfaces = Instance(klass=Config, kw=default_client_interfaces,
102 interfaces = Instance(klass=Config, kw=default_client_interfaces,
103 allow_none=False, config=True)
103 allow_none=False, config=True)
104
104
105
105
106 class FCEngineServiceFactory(FCServiceFactory):
106 class FCEngineServiceFactory(FCServiceFactory):
107 """A Foolscap implementation of the engine services."""
107 """A Foolscap implementation of the engine services."""
108
108
109 cert_file = Unicode(u'ipcontroller-engine.pem', config=True)
109 cert_file = Unicode(u'ipcontroller-engine.pem', config=True)
110 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
110 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
111 allow_none=False, config=True)
111 allow_none=False, config=True)
112
112
113
113
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115 # Command line options
115 # Command line options
116 #-----------------------------------------------------------------------------
116 #-----------------------------------------------------------------------------
117
117
118
118
119 class IPControllerAppConfigLoader(ClusterDirConfigLoader):
119 class IPControllerAppConfigLoader(ClusterDirConfigLoader):
120
120
121 def _add_arguments(self):
121 def _add_arguments(self):
122 super(IPControllerAppConfigLoader, self)._add_arguments()
122 super(IPControllerAppConfigLoader, self)._add_arguments()
123 paa = self.parser.add_argument
123 paa = self.parser.add_argument
124 # Client config
124 # Client config
125 paa('--client-ip',
125 paa('--client-ip',
126 type=str, dest='FCClientServiceFactory.ip',
126 type=str, dest='FCClientServiceFactory.ip',
127 help='The IP address or hostname the controller will listen on for '
127 help='The IP address or hostname the controller will listen on for '
128 'client connections.',
128 'client connections.',
129 metavar='FCClientServiceFactory.ip')
129 metavar='FCClientServiceFactory.ip')
130 paa('--client-port',
130 paa('--client-port',
131 type=int, dest='FCClientServiceFactory.port',
131 type=int, dest='FCClientServiceFactory.port',
132 help='The port the controller will listen on for client connections. '
132 help='The port the controller will listen on for client connections. '
133 'The default is to use 0, which will autoselect an open port.',
133 'The default is to use 0, which will autoselect an open port.',
134 metavar='FCClientServiceFactory.port')
134 metavar='FCClientServiceFactory.port')
135 paa('--client-location',), dict(
135 paa('--client-location',), dict(
136 type=str, dest='FCClientServiceFactory.location',
136 type=str, dest='FCClientServiceFactory.location',
137 help='The hostname or IP that clients should connect to. This does '
137 help='The hostname or IP that clients should connect to. This does '
138 'not control which interface the controller listens on. Instead, this '
138 'not control which interface the controller listens on. Instead, this '
139 'determines the hostname/IP that is listed in the FURL, which is how '
139 'determines the hostname/IP that is listed in the FURL, which is how '
140 'clients know where to connect. Useful if the controller is listening '
140 'clients know where to connect. Useful if the controller is listening '
141 'on multiple interfaces.',
141 'on multiple interfaces.',
142 metavar='FCClientServiceFactory.location')
142 metavar='FCClientServiceFactory.location')
143 # Engine config
143 # Engine config
144 paa('--engine-ip',
144 paa('--engine-ip',
145 type=str, dest='FCEngineServiceFactory.ip',
145 type=str, dest='FCEngineServiceFactory.ip',
146 help='The IP address or hostname the controller will listen on for '
146 help='The IP address or hostname the controller will listen on for '
147 'engine connections.',
147 'engine connections.',
148 metavar='FCEngineServiceFactory.ip')
148 metavar='FCEngineServiceFactory.ip')
149 paa('--engine-port',
149 paa('--engine-port',
150 type=int, dest='FCEngineServiceFactory.port',
150 type=int, dest='FCEngineServiceFactory.port',
151 help='The port the controller will listen on for engine connections. '
151 help='The port the controller will listen on for engine connections. '
152 'The default is to use 0, which will autoselect an open port.',
152 'The default is to use 0, which will autoselect an open port.',
153 metavar='FCEngineServiceFactory.port')
153 metavar='FCEngineServiceFactory.port')
154 paa('--engine-location',
154 paa('--engine-location',
155 type=str, dest='FCEngineServiceFactory.location',
155 type=str, dest='FCEngineServiceFactory.location',
156 help='The hostname or IP that engines should connect to. This does '
156 help='The hostname or IP that engines should connect to. This does '
157 'not control which interface the controller listens on. Instead, this '
157 'not control which interface the controller listens on. Instead, this '
158 'determines the hostname/IP that is listed in the FURL, which is how '
158 'determines the hostname/IP that is listed in the FURL, which is how '
159 'engines know where to connect. Useful if the controller is listening '
159 'engines know where to connect. Useful if the controller is listening '
160 'on multiple interfaces.',
160 'on multiple interfaces.',
161 metavar='FCEngineServiceFactory.location')
161 metavar='FCEngineServiceFactory.location')
162 # Global config
162 # Global config
163 paa('--log-to-file',
163 paa('--log-to-file',
164 action='store_true', dest='Global.log_to_file',
164 action='store_true', dest='Global.log_to_file',
165 help='Log to a file in the log directory (default is stdout)')
165 help='Log to a file in the log directory (default is stdout)')
166 paa('-r','--reuse-furls',
166 paa('-r','--reuse-furls',
167 action='store_true', dest='Global.reuse_furls',
167 action='store_true', dest='Global.reuse_furls',
168 help='Try to reuse all FURL files. If this is not set all FURL files '
168 help='Try to reuse all FURL files. If this is not set all FURL files '
169 'are deleted before the controller starts. This must be set if '
169 'are deleted before the controller starts. This must be set if '
170 'specific ports are specified by --engine-port or --client-port.')
170 'specific ports are specified by --engine-port or --client-port.')
171 paa('--no-secure',
171 paa('--no-secure',
172 action='store_false', dest='Global.secure',
172 action='store_false', dest='Global.secure',
173 help='Turn off SSL encryption for all connections.')
173 help='Turn off SSL encryption for all connections.')
174 paa('--secure',
174 paa('--secure',
175 action='store_true', dest='Global.secure',
175 action='store_true', dest='Global.secure',
176 help='Turn off SSL encryption for all connections.')
176 help='Turn off SSL encryption for all connections.')
177
177
178
178
179 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
180 # The main application
180 # The main application
181 #-----------------------------------------------------------------------------
181 #-----------------------------------------------------------------------------
182
182
183
183
184 class IPControllerApp(ApplicationWithClusterDir):
184 class IPControllerApp(ApplicationWithClusterDir):
185
185
186 name = u'ipcontroller'
186 name = u'ipcontroller'
187 description = _description
187 description = _description
188 command_line_loader = IPControllerAppConfigLoader
188 command_line_loader = IPControllerAppConfigLoader
189 default_config_file_name = default_config_file_name
189 default_config_file_name = default_config_file_name
190 auto_create_cluster_dir = True
190 auto_create_cluster_dir = True
191
191
192 def create_default_config(self):
192 def create_default_config(self):
193 super(IPControllerApp, self).create_default_config()
193 super(IPControllerApp, self).create_default_config()
194 # Don't set defaults for Global.secure or Global.reuse_furls
194 # Don't set defaults for Global.secure or Global.reuse_furls
195 # as those are set in a component.
195 # as those are set in a component.
196 self.default_config.Global.import_statements = []
196 self.default_config.Global.import_statements = []
197 self.default_config.Global.clean_logs = True
197 self.default_config.Global.clean_logs = True
198
198
199 def pre_construct(self):
199 def pre_construct(self):
200 super(IPControllerApp, self).pre_construct()
200 super(IPControllerApp, self).pre_construct()
201 c = self.master_config
201 c = self.master_config
202 # The defaults for these are set in FCClientServiceFactory and
202 # The defaults for these are set in FCClientServiceFactory and
203 # FCEngineServiceFactory, so we only set them here if the global
203 # FCEngineServiceFactory, so we only set them here if the global
204 # options have be set to override the class level defaults.
204 # options have be set to override the class level defaults.
205 if hasattr(c.Global, 'reuse_furls'):
205 if hasattr(c.Global, 'reuse_furls'):
206 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
206 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
207 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
207 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
208 del c.Global.reuse_furls
208 del c.Global.reuse_furls
209 if hasattr(c.Global, 'secure'):
209 if hasattr(c.Global, 'secure'):
210 c.FCClientServiceFactory.secure = c.Global.secure
210 c.FCClientServiceFactory.secure = c.Global.secure
211 c.FCEngineServiceFactory.secure = c.Global.secure
211 c.FCEngineServiceFactory.secure = c.Global.secure
212 del c.Global.secure
212 del c.Global.secure
213
213
214 def construct(self):
214 def construct(self):
215 # This is the working dir by now.
215 # This is the working dir by now.
216 sys.path.insert(0, '')
216 sys.path.insert(0, '')
217
217
218 self.start_logging()
218 self.start_logging()
219 self.import_statements()
219 self.import_statements()
220
220
221 # Create the service hierarchy
221 # Create the service hierarchy
222 self.main_service = service.MultiService()
222 self.main_service = service.MultiService()
223 # The controller service
223 # The controller service
224 controller_service = controllerservice.ControllerService()
224 controller_service = controllerservice.ControllerService()
225 controller_service.setServiceParent(self.main_service)
225 controller_service.setServiceParent(self.main_service)
226 # The client tub and all its refereceables
226 # The client tub and all its refereceables
227 try:
227 try:
228 csfactory = FCClientServiceFactory(self.master_config, controller_service)
228 csfactory = FCClientServiceFactory(config=self.master_config, adaptee=controller_service)
229 except FURLError, e:
229 except FURLError, e:
230 log.err(e)
230 log.err(e)
231 self.exit(0)
231 self.exit(0)
232 client_service = csfactory.create()
232 client_service = csfactory.create()
233 client_service.setServiceParent(self.main_service)
233 client_service.setServiceParent(self.main_service)
234 # The engine tub
234 # The engine tub
235 try:
235 try:
236 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
236 esfactory = FCEngineServiceFactory(config=self.master_config, adaptee=controller_service)
237 except FURLError, e:
237 except FURLError, e:
238 log.err(e)
238 log.err(e)
239 self.exit(0)
239 self.exit(0)
240 engine_service = esfactory.create()
240 engine_service = esfactory.create()
241 engine_service.setServiceParent(self.main_service)
241 engine_service.setServiceParent(self.main_service)
242
242
243 def import_statements(self):
243 def import_statements(self):
244 statements = self.master_config.Global.import_statements
244 statements = self.master_config.Global.import_statements
245 for s in statements:
245 for s in statements:
246 try:
246 try:
247 log.msg("Executing statement: '%s'" % s)
247 log.msg("Executing statement: '%s'" % s)
248 exec s in globals(), locals()
248 exec s in globals(), locals()
249 except:
249 except:
250 log.msg("Error running statement: %s" % s)
250 log.msg("Error running statement: %s" % s)
251
251
252 def start_app(self):
252 def start_app(self):
253 # Start the controller service.
253 # Start the controller service.
254 self.main_service.startService()
254 self.main_service.startService()
255 # Write the .pid file overwriting old ones. This allow multiple
255 # Write the .pid file overwriting old ones. This allow multiple
256 # controllers to clober each other. But Windows is not cleaning
256 # controllers to clober each other. But Windows is not cleaning
257 # these up properly.
257 # these up properly.
258 self.write_pid_file(overwrite=True)
258 self.write_pid_file(overwrite=True)
259 # Add a trigger to delete the .pid file upon shutting down.
259 # Add a trigger to delete the .pid file upon shutting down.
260 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
260 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
261 reactor.run()
261 reactor.run()
262
262
263
263
264 def launch_new_instance():
264 def launch_new_instance():
265 """Create and run the IPython controller"""
265 """Create and run the IPython controller"""
266 app = IPControllerApp()
266 app = IPControllerApp()
267 app.start()
267 app.start()
268
268
269
269
270 if __name__ == '__main__':
270 if __name__ == '__main__':
271 launch_new_instance()
271 launch_new_instance()
General Comments 0
You need to be logged in to leave comments. Login now