Mercurial > bitten > bitten-test
annotate bitten/util/beep.py @ 93:b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
author | cmlenz |
---|---|
date | Fri, 15 Jul 2005 13:59:54 +0000 |
parents | 2e090827c63a |
children | 1984b2e01998 |
rev | line source |
---|---|
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
1 # -*- coding: iso8859-1 -*- |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
2 # |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
3 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
4 # |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
5 # Bitten is free software; you can redistribute it and/or |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
6 # modify it under the terms of the GNU General Public License as |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
7 # published by the Free Software Foundation; either version 2 of the |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
8 # License, or (at your option) any later version. |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
9 # |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
10 # Trac is distributed in the hope that it will be useful, |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
13 # General Public License for more details. |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
14 # |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
15 # You should have received a copy of the GNU General Public License |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
16 # along with this program; if not, write to the Free Software |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
18 # |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
19 # Author: Christopher Lenz <cmlenz@gmx.de> |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
20 |
11 | 21 """Minimal implementation of the BEEP protocol (IETF RFC 3080) based on the |
22 `asyncore` module. | |
23 | |
24 Current limitations: | |
25 * No support for the TSL and SASL profiles. | |
33
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
26 * No support for mapping frames (SEQ frames for TCP mapping). |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
27 * No localization support (xml:lang attribute). |
11 | 28 """ |
29 | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
30 import asynchat |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
31 import asyncore |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
32 import bisect |
29 | 33 import email |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
34 import logging |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
35 import socket |
83
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
80
diff
changeset
|
36 try: |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
80
diff
changeset
|
37 set |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
80
diff
changeset
|
38 except NameError: |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
80
diff
changeset
|
39 from sets import Set as set |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
40 try: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
41 from cStringIO import StringIO |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
42 except ImportError: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
43 from StringIO import StringIO |
10 | 44 import sys |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
45 import time |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
46 |
29 | 47 from bitten.util import xmlio |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
48 |
88 | 49 __all__ = ['Listener', 'Initiator', 'Payload', 'ProfileHandler', |
80
dc1c7fc9b915
Record the output of build steps in the database. See #12. Still need to get better granularity in transmitting the log output from slave to master before #12 can be closed.
cmlenz
parents:
62
diff
changeset
|
50 'ProtocolError'] |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
51 |
11 | 52 BEEP_XML = 'application/beep+xml' |
53 | |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
54 log = logging.getLogger('bitten.beep') |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
55 |
11 | 56 |
57 class ProtocolError(Exception): | |
58 """Generic root class for BEEP exceptions.""" | |
59 | |
60 | |
13
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
61 class TerminateSession(Exception): |
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
62 """Signal termination of a session.""" |
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
63 |
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
64 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
65 class Listener(asyncore.dispatcher): |
10 | 66 """BEEP peer in the listener role. |
67 | |
68 This peer opens a socket for listening to incoming connections. For each | |
69 connection, it opens a new session: an instance of `Session` that handle | |
70 communication with the connected peer. | |
71 """ | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
72 def __init__(self, ip, port): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
73 asyncore.dispatcher.__init__(self) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
74 self.create_socket(socket.AF_INET, socket.SOCK_STREAM) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
75 self.set_reuse_addr() |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
76 self.bind((ip, port)) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
77 self.sessions = [] |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
78 self.profiles = {} # Mapping from URIs to ProfileHandler sub-classes |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
79 self.eventqueue = [] |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
80 log.debug('Listening to connections on %s:%d', ip, port) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
81 self.listen(5) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
82 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
83 def writable(self): |
29 | 84 """Called by asyncore to determine whether the channel is writable.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
85 return False |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
86 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
87 def handle_read(self): |
29 | 88 """Called by asyncore to signal data available for reading.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
89 pass |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
90 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
91 def readable(self): |
29 | 92 """Called by asyncore to determine whether the channel is readable.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
93 return True |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
94 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
95 def handle_accept(self): |
29 | 96 """Start a new BEEP session initiated by a peer.""" |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
97 conn, (ip, port) = self.accept() |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
98 log.debug('Connected to %s:%d', ip, port) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
99 self.sessions.append(Session(self, conn, (ip, port), self.profiles, |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
100 first_channelno=2)) |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
101 |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
102 def run(self, timeout=15.0, granularity=5): |
29 | 103 """Start listening to incoming connections.""" |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
104 socket_map = asyncore.socket_map |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
105 last_event_check = 0 |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
106 while socket_map: |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
107 now = int(time.time()) |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
108 if (now - last_event_check) >= granularity: |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
109 last_event_check = now |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
110 fired = [] |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
111 i = j = 0 |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
112 while i < len(self.eventqueue): |
29 | 113 when, callback = self.eventqueue[i] |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
114 if now >= when: |
29 | 115 fired.append(callback) |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
116 j = i + 1 |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
117 else: |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
118 break |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
119 i = i + 1 |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
120 if fired: |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
121 self.eventqueue = self.eventqueue[j:] |
29 | 122 for callback in fired: |
92
2e090827c63a
Delete snapshots for builds that have been completed by all configured target platforms, and are thus no longer needed. Closes #20.
cmlenz
parents:
90
diff
changeset
|
123 callback(now) |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
124 asyncore.poll(timeout) |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
125 |
29 | 126 def schedule(self, delta, callback): |
127 """Schedule a function to be called. | |
128 | |
129 @param delta: The number of seconds after which the callback should be | |
130 invoked | |
131 @param callback: The function to call | |
132 """ | |
133 bisect.insort(self.eventqueue, (int(time.time()) + delta, callback)) | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
134 |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
135 def quit(self): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
136 if not self.sessions: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
137 self.close() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
138 return |
92
2e090827c63a
Delete snapshots for builds that have been completed by all configured target platforms, and are thus no longer needed. Closes #20.
cmlenz
parents:
90
diff
changeset
|
139 def terminate_next_session(when): |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
140 session = self.sessions[-1] |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
141 def handle_ok(): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
142 if self.sessions: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
143 terminate_next_session() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
144 else: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
145 self.close() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
146 def handle_error(channelno, code, message): |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
147 log.error('Failed to close channel %d', channelno) |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
148 log.debug('Closing session with %s', session.addr) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
149 session.terminate(handle_ok=handle_ok) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
150 self.schedule(0, terminate_next_session) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
151 self.run(.5) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
152 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
153 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
154 class Session(asynchat.async_chat): |
10 | 155 """A BEEP session between two peers.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
156 |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
157 def __init__(self, listener=None, conn=None, addr=None, profiles=None, |
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
158 first_channelno=1): |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
159 """Create the BEEP session. |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
160 |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
161 @param listener: The `Listener` this session belongs to, or `None` for |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
162 a session by the initiating peer |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
163 @param conn: The connection |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
164 @param addr: The address of the remote peer, a (IP-address, port) tuple |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
165 @param profiles: A dictionary of supported profiles; the keys are the |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
166 URIs of the profiles, the values are corresponding |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
167 sub-classes of `ProfileHandler` |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
168 @param first_channelno: The first channel number to request; 0 for the |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
169 peer in the listening role, 1 for initiators |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
170 """ |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
171 asynchat.async_chat.__init__(self, conn) |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
172 self.listener = listener |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
173 self.addr = addr |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
174 self.set_terminator('\r\n') |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
175 |
10 | 176 self.profiles = profiles or {} |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
177 self.inbuf = [] |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
178 self.header = self.payload = None |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
179 |
11 | 180 self.channelno = cycle_through(first_channelno, 2147483647, step=2) |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
181 self.channels = {0: Channel(self, 0, ManagementProfileHandler)} |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
182 |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
183 def close(self): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
184 if self.listener: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
185 log.debug('Closing connection to %s:%s', self.addr[0], self.addr[1]) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
186 self.listener.sessions.remove(self) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
187 else: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
188 log.info('Session terminated') |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
189 asynchat.async_chat.close(self) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
190 |
62 | 191 def handle_close(self): |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
192 log.warning('Peer %s:%s closed connection' % self.addr) |
62 | 193 channels = self.channels.keys() |
194 channels.reverse() | |
195 for channelno in channels: | |
196 self.channels[channelno].close() | |
197 asynchat.async_chat.handle_close(self) | |
198 | |
10 | 199 def handle_error(self): |
13
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
200 """Called by asyncore when an exception is raised.""" |
29 | 201 cls, value = sys.exc_info()[:2] |
202 if cls is TerminateSession: | |
203 raise cls, value | |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
204 log.exception(value) |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
205 self.close() |
10 | 206 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
207 def collect_incoming_data(self, data): |
13
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
208 """Called by async_chat when data is received. |
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
209 |
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
12
diff
changeset
|
210 Buffer the data and wait until a terminator is found.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
211 self.inbuf.append(data) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
212 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
213 def found_terminator(self): |
10 | 214 """Called by async_chat when a terminator is found in the input |
11 | 215 stream. |
216 | |
217 Parse the incoming data depending on whether it terminated on the frame | |
218 header, playload or trailer. For the header, extract the payload size | |
219 parameter and use it as terminator or the payload. When the trailer has | |
220 been received, delegate to `_handle_frame()`. | |
221 """ | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
222 if self.header is None: |
11 | 223 # Frame header received |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
224 self.header = ''.join(self.inbuf).split(' ') |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
225 self.inbuf = [] |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
226 if self.header[0] == 'SEQ': |
23
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
227 size = 0 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
228 else: |
11 | 229 # Extract payload size to use as next terminator |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
230 try: |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
231 size = int(self.header[int(self.header[0] != 'ANS') - 2]) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
232 except ValueError: |
11 | 233 # TODO: Malformed frame... should we terminate the session |
234 # here? | |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
235 log.error('Malformed frame header: [%s]', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
236 ' '.join(self.header)) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
237 self.header = None |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
238 return |
23
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
239 if size == 0: |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
240 self.payload = '' |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
241 self.set_terminator('END\r\n') |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
242 else: |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
243 self.set_terminator(size) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
244 elif self.payload is None: |
11 | 245 # Frame payload received |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
246 self.payload = ''.join(self.inbuf) |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
247 self.inbuf = [] |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
248 self.set_terminator('END\r\n') |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
249 else: |
11 | 250 # Frame trailer received |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
251 try: |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
252 self._handle_frame(self.header, self.payload) |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
253 finally: |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
254 self.header = self.payload = None |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
255 self.inbuf = [] |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
256 self.set_terminator('\r\n') |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
257 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
258 def _handle_frame(self, header, payload): |
10 | 259 """Handle an incoming frame. |
260 | |
261 This parses the frame header and decides which channel to pass it to. | |
262 """ | |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
263 log.debug('Handling frame [%s]', ' '.join(header)) |
11 | 264 msgno = None |
265 channel = None | |
266 try: | |
267 cmd = header[0].upper() | |
268 channel = int(header[1]) | |
23
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
269 if cmd == 'SEQ': |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
270 ackno = int(header[2]) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
271 window = int(header[3]) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
272 self.channels[channel].handle_seq_frame(ackno, window) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
273 else: |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
274 msgno = int(header[2]) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
275 more = header[3] == '*' |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
276 seqno = int(header[4]) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
277 ansno = None |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
278 if cmd == 'ANS': |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
279 ansno = int(header[6]) |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
280 self.channels[channel].handle_data_frame(cmd, msgno, more, |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
281 seqno, ansno, payload) |
11 | 282 except (ValueError, TypeError, ProtocolError), e: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
283 log.exception(e) |
11 | 284 if channel == 0 and msgno is not None: |
285 self.channels[0].profile.send_error(msgno, 550, e) | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
286 |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
287 def terminate(self, handle_ok=None, handle_error=None): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
288 """Terminate the session by closing all channels.""" |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
289 def close_next_channel(): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
290 channelno = max(self.channels.keys()) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
291 def _handle_ok(): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
292 if channelno == 0: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
293 if handle_ok is not None: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
294 handle_ok() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
295 else: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
296 close_next_channel() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
297 def _handle_error(code, message): |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
298 log.error('Peer refused to close channel %d: %s (%d)', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
299 channelno, message, code) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
300 if handle_error is not None: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
301 handle_error(channelno, code, message) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
302 else: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
303 raise ProtocolError, '%s (%d)' % (message, code) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
304 self.channels[0].profile.send_close(channelno, handle_ok=_handle_ok, |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
305 handle_error=_handle_error) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
306 close_next_channel() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
307 |
11 | 308 |
309 class Initiator(Session): | |
310 """Root class for BEEP peers in the initiating role.""" | |
311 | |
312 def __init__(self, ip, port, profiles=None): | |
313 """Create the BEEP session. | |
314 | |
315 @param ip: The IP address to connect to | |
316 @param port: The port to connect to | |
317 @param profiles: A dictionary of the supported profiles, where the key | |
318 is the URI identifying the profile, and the value is a | |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
319 `ProfileHandler` sub-class that will be instantiated to |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
320 handle the communication for that profile |
11 | 321 """ |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
322 Session.__init__(self, profiles=profiles or {}) |
11 | 323 self.create_socket(socket.AF_INET, socket.SOCK_STREAM) |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
324 log.debug('Connecting to %s:%s', ip, port) |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
325 self.addr = (ip, port) |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
326 self.connect(self.addr) |
10 | 327 |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
328 def handle_connect(self): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
329 """Called by asyncore when the connection is established.""" |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
330 log.debug('Connected to peer at %s:%s', self.addr[0], self.addr[1]) |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
331 |
10 | 332 def greeting_received(self, profiles): |
11 | 333 """Sub-classes should override this to start the channels they need. |
10 | 334 |
335 @param profiles: A list of URIs of the profiles the peer claims to | |
336 support. | |
337 """ | |
29 | 338 pass |
10 | 339 |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
340 def run(self): |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
341 """Start this peer, which will try to connect to the server and send a |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
342 greeting. |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
343 """ |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
344 try: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
345 asyncore.loop() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
346 except TerminateSession, e: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
347 log.info('Terminating session') |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
348 self.terminate() |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
349 |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
350 def quit(self): |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
351 self.terminate() |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
352 asyncore.loop(timeout=10) |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
353 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
354 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
355 class Channel(object): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
356 """A specific channel of a BEEP session.""" |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
357 |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
358 def __init__(self, session, channelno, profile_cls): |
11 | 359 """Create the channel. |
360 | |
361 @param session The `Session` object that the channel belongs to | |
362 @param channelno The channel number | |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
363 @param profile The associated `ProfileHandler` class |
11 | 364 """ |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
365 self.session = session |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
366 self.channelno = channelno |
23
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
367 self.windowsize = 4096 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
368 self.inqueue = {} |
10 | 369 self.reply_handlers = {} |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
370 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
371 self.msgno = cycle_through(0, 2147483647) |
83
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
80
diff
changeset
|
372 self.msgnos = set() # message numbers currently in use |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
373 self.ansnos = {} # answer numbers keyed by msgno, each 0-2147483647 |
29 | 374 |
375 # incoming, outgoing sequence numbers | |
376 self.seqno = [SerialNumber(), SerialNumber()] | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
377 |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
378 self.profile = profile_cls(self) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
379 self.profile.handle_connect() |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
380 |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
381 def close(self): |
29 | 382 """Close the channel.""" |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
383 self.profile.handle_disconnect() |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
384 del self.session.channels[self.channelno] |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
385 |
23
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
386 def handle_seq_frame(self, ackno, window): |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
387 """Process a TCP mapping frame (SEQ). |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
388 |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
389 @param ackno: the value of the next sequence number that the sender is |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
390 expecting to receive on this channel |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
391 @param window: window size, the number of payload octets per frame that |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
392 the sender is expecting to receive on this channel |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
393 """ |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
394 self.windowsize = window |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
395 |
1a7b9044b0a5
Basic TCP mapping and message fragmenting support. See #1.
cmlenz
parents:
18
diff
changeset
|
396 def handle_data_frame(self, cmd, msgno, more, seqno, ansno, payload): |
11 | 397 """Process a single data frame. |
398 | |
399 @param cmd: The frame keyword (MSG, RPY, ERR, ANS or NUL) | |
400 @param msgno: The message number | |
401 @param more: `True` if more frames are pending for this message | |
402 @param seqno: Sequence number of the frame | |
403 @param ansno: The answer number for 'ANS' messages, otherwise `None` | |
404 @param payload: The frame payload as a string | |
405 """ | |
10 | 406 # Validate and update sequence number |
407 if seqno != self.seqno[0]: | |
11 | 408 raise ProtocolError, 'Out of sync with peer' # TODO: Be nice |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
409 self.seqno[0] += len(payload) |
10 | 410 |
411 if more: | |
412 # More of this message pending, so push it on the queue | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
413 self.inqueue.setdefault(msgno, []).append(payload) |
10 | 414 return |
415 | |
416 # Complete message received, so handle it | |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
417 if msgno in self.inqueue: |
10 | 418 # Recombine queued messages |
419 payload = ''.join(self.inqueue[msgno]) + payload | |
420 del self.inqueue[msgno] | |
62 | 421 if cmd in ('ERR', 'RPY', 'NUL') and msgno in self.msgnos: |
10 | 422 # Final reply using this message number, so dealloc |
46
f7bbf9d2bbe7
Use a set instead of a dictionary for keeping track of message numbers.
cmlenz
parents:
39
diff
changeset
|
423 self.msgnos.remove(msgno) |
10 | 424 if payload: |
88 | 425 payload = Payload.parse(payload) |
426 else: | |
427 payload = None | |
10 | 428 |
429 if cmd == 'MSG': | |
88 | 430 self.profile.handle_msg(msgno, payload) |
10 | 431 else: |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
432 if msgno in self.reply_handlers: |
29 | 433 try: |
88 | 434 self.reply_handlers[msgno](cmd, msgno, ansno, payload) |
29 | 435 finally: |
53 | 436 if cmd != 'ANS': |
437 del self.reply_handlers[msgno] | |
10 | 438 elif cmd == 'RPY': |
88 | 439 self.profile.handle_rpy(msgno, payload) |
10 | 440 elif cmd == 'ERR': |
88 | 441 self.profile.handle_err(msgno, payload) |
10 | 442 elif cmd == 'ANS': |
88 | 443 self.profile.handle_ans(msgno, ansno, payload) |
10 | 444 elif cmd == 'NUL': |
445 self.profile.handle_nul(msgno) | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
446 |
88 | 447 def send_msg(self, payload, handle_reply=None): |
448 """Send a MSG frame to the peer. | |
449 | |
450 @param payload: The message payload (a `Payload` instance) | |
451 @param handle_reply: A function that is called when a reply to this | |
452 message is received | |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
453 @return: the message number assigned to the message |
88 | 454 """ |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
455 while True: # Find a unique message number |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
456 msgno = self.msgno.next() |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
457 if msgno not in self.msgnos: |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
458 break |
46
f7bbf9d2bbe7
Use a set instead of a dictionary for keeping track of message numbers.
cmlenz
parents:
39
diff
changeset
|
459 self.msgnos.add(msgno) # Flag the chosen message number as in use |
10 | 460 if handle_reply is not None: |
461 self.reply_handlers[msgno] = handle_reply | |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
462 self.session.push_with_producer(FrameProducer(self, 'MSG', msgno, None, |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
463 payload)) |
10 | 464 return msgno |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
465 |
88 | 466 def send_rpy(self, msgno, payload): |
29 | 467 """Send a RPY frame to the peer. |
468 | |
469 @param msgno: The number of the message this reply is in reference to | |
88 | 470 @param payload: The message payload (a `Payload` instance) |
29 | 471 """ |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
472 self.session.push_with_producer(FrameProducer(self, 'RPY', msgno, None, |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
473 payload)) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
474 |
88 | 475 def send_err(self, msgno, payload): |
29 | 476 """Send an ERR frame to the peer. |
477 | |
478 @param msgno: The number of the message this reply is in reference to | |
88 | 479 @param payload: The message payload (a `Payload` instance) |
29 | 480 """ |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
481 self.session.push_with_producer(FrameProducer(self, 'ERR', msgno, None, |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
482 payload)) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
483 |
88 | 484 def send_ans(self, msgno, payload): |
29 | 485 """Send an ANS frame to the peer. |
486 | |
487 @param msgno: The number of the message this reply is in reference to | |
88 | 488 @param payload: The message payload (a `Payload` instance) |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
489 @return: the answer number assigned to the answer |
29 | 490 """ |
491 ansnos = self.ansnos.setdefault(msgno, cycle_through(0, 2147483647)) | |
492 next_ansno = ansnos.next() | |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
493 self.session.push_with_producer(FrameProducer(self, 'ANS', msgno, |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
494 next_ansno, payload)) |
10 | 495 return next_ansno |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
496 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
497 def send_nul(self, msgno): |
29 | 498 """Send a NUL frame to the peer. |
499 | |
500 @param msgno: The number of the message this reply is in reference to | |
501 """ | |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
502 self.session.push_with_producer(FrameProducer(self, 'NUL', msgno)) |
10 | 503 del self.ansnos[msgno] # dealloc answer numbers for the message |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
504 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
505 |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
506 class FrameProducer(object): |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
507 """Internal class that emits the frames of a BEEP message, based on the |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
508 `asynchat` `push_with_producer()` protocol. |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
509 """ |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
510 |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
511 def __init__(self, channel, cmd, msgno, ansno=None, payload=None): |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
512 """Initialize the frame producer. |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
513 |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
514 @param channel the channel the message is to be sent on |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
515 @param cmd the BEEP command/keyword (MSG, RPY, ERR, ANS or NUL) |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
516 @param msgno the message number |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
517 @param ansno the answer number (only for ANS messages) |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
518 @param payload the message payload (an instance of `Payload`) |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
519 """ |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
520 self.session = channel.session |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
521 self.channel = channel |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
522 self.cmd = cmd |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
523 self.msgno = msgno |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
524 self.ansno = ansno |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
525 |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
526 self.payload = payload |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
527 self.done = False |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
528 |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
529 def more(self): |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
530 """Called by `async_chat` when the producer has been pushed on the |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
531 producer FIFO and the channel is about to write.""" |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
532 if self.done: |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
533 return '' |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
534 |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
535 if self.payload: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
536 data = self.payload.read(self.channel.windowsize) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
537 if len(data) < self.channel.windowsize: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
538 self.done = True |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
539 else: |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
540 data = '' |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
541 self.done = True |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
542 |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
543 headerbits = [self.cmd, self.channel.channelno, self.msgno, |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
544 self.done and '.' or '*', self.channel.seqno[1].value, |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
545 len(data)] |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
546 if self.cmd == 'ANS': |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
547 assert self.ansno is not None |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
548 headerbits.append(self.ansno) |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
549 header = ' '.join([str(bit) for bit in headerbits]) |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
550 log.debug('Sending frame [%s]', header) |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
551 frame = '\r\n'.join((header, data, 'END', '')) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
552 self.channel.seqno[1] += len(data) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
553 |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
554 return frame |
89
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
555 |
7b10a8b2d0c4
Changed BEEP frame sending mechanism to use the `push_with_producer()` function provided by `asynchat`. In preparation for #17.
cmlenz
parents:
88
diff
changeset
|
556 |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
557 class ProfileHandler(object): |
11 | 558 """Abstract base class for handlers of specific BEEP profiles. |
559 | |
560 Concrete subclasses need to at least implement the `handle_msg()` method, | |
561 and may override any of the others. | |
562 """ | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
563 |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
564 def __init__(self, channel): |
11 | 565 """Create the profile.""" |
27
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
566 self.session = channel.session |
a8e509f4d33b
Added a couple of unit tests for the BEEP management profile. See #2. More to come.
cmlenz
parents:
24
diff
changeset
|
567 self.channel = channel |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
568 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
569 def handle_connect(self): |
11 | 570 """Called when the channel this profile is associated with is |
571 initially started.""" | |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
572 |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
573 def handle_disconnect(self): |
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
574 """Called when the channel this profile is associated with is closed.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
575 |
10 | 576 def handle_msg(self, msgno, message): |
29 | 577 """Handle a MSG frame.""" |
10 | 578 raise NotImplementedError |
579 | |
580 def handle_rpy(self, msgno, message): | |
29 | 581 """Handle a RPY frame.""" |
11 | 582 pass |
10 | 583 |
584 def handle_err(self, msgno, message): | |
29 | 585 """Handle an ERR frame.""" |
11 | 586 pass |
10 | 587 |
588 def handle_ans(self, msgno, ansno, message): | |
29 | 589 """Handle an ANS frame.""" |
11 | 590 pass |
10 | 591 |
592 def handle_nul(self, msgno): | |
29 | 593 """Handle a NUL frame.""" |
11 | 594 pass |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
595 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
596 |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
597 class ManagementProfileHandler(ProfileHandler): |
11 | 598 """Implementation of the BEEP management profile.""" |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
599 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
600 def handle_connect(self): |
11 | 601 """Send a greeting reply directly after connecting to the peer.""" |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
602 profile_uris = self.session.profiles.keys() |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
603 log.debug('Send greeting with profiles: %s', profile_uris) |
29 | 604 xml = xmlio.Element('greeting')[ |
605 [xmlio.Element('profile', uri=uri) for uri in profile_uris] | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
606 ] |
88 | 607 self.channel.send_rpy(0, Payload(xml)) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
608 |
10 | 609 def handle_msg(self, msgno, message): |
29 | 610 """Handle an incoming message.""" |
88 | 611 assert message.content_type == BEEP_XML |
612 elem = xmlio.parse(message.body) | |
11 | 613 |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
614 if elem.name == 'start': |
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
615 channelno = int(elem.attr['number']) |
28 | 616 if channelno in self.session.channels: |
617 self.send_error(msgno, 550, 'Channel already in use') | |
618 return | |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
619 for profile in elem.children('profile'): |
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
620 if profile.attr['uri'] in self.session.profiles: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
621 log.debug('Start channel %s for profile <%s>', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
622 elem.attr['number'], profile.attr['uri']) |
28 | 623 channel = Channel(self.session, channelno, |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
624 self.session.profiles[profile.attr['uri']]) |
28 | 625 self.session.channels[channelno] = channel |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
626 xml = xmlio.Element('profile', uri=profile.attr['uri']) |
88 | 627 self.channel.send_rpy(msgno, Payload(xml)) |
11 | 628 return |
629 self.send_error(msgno, 550, | |
28 | 630 'None of the requested profiles is supported') |
11 | 631 |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
632 elif elem.name == 'close': |
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
633 channelno = int(elem.attr['number']) |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
634 if not channelno in self.session.channels: |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
635 self.send_error(msgno, 550, 'Channel not open') |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
636 return |
11 | 637 if channelno == 0: |
638 if len(self.session.channels) > 1: | |
639 self.send_error(msgno, 550, 'Other channels still open') | |
640 return | |
641 if self.session.channels[channelno].msgnos: | |
642 self.send_error(msgno, 550, 'Channel waiting for replies') | |
643 return | |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
644 self.session.channels[channelno].close() |
88 | 645 self.channel.send_rpy(msgno, Payload(xmlio.Element('ok'))) |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
646 if not self.session.channels: |
11 | 647 self.session.close() |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
648 |
10 | 649 def handle_rpy(self, msgno, message): |
29 | 650 """Handle a positive reply.""" |
88 | 651 assert message.content_type == BEEP_XML |
652 elem = xmlio.parse(message.body) | |
11 | 653 |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
654 if elem.name == 'greeting': |
11 | 655 if isinstance(self.session, Initiator): |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
656 profiles = [p.attr['uri'] for p in elem.children('profile')] |
11 | 657 self.session.greeting_received(profiles) |
658 | |
659 else: # <profile/> and <ok/> are handled by callbacks | |
660 self.send_error(msgno, 501, 'What are you replying to, son?') | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
661 |
10 | 662 def handle_err(self, msgno, message): |
29 | 663 """Handle a negative reply.""" |
11 | 664 # Probably an error on connect, because other errors should get handled |
665 # by the corresponding callbacks | |
666 # TODO: Terminate the session, I guess | |
88 | 667 assert message.content_type == BEEP_XML |
668 elem = xmlio.parse(message.body) | |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
669 assert elem.name == 'error' |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
670 log.warning('Received error in response to message #%d: %s (%d)', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
671 msgno, elem.gettext(), int(elem.attr['code'])) |
10 | 672 |
11 | 673 def send_close(self, channelno=0, code=200, handle_ok=None, |
674 handle_error=None): | |
29 | 675 """Send a request to close a channel to the peer.""" |
53 | 676 def handle_reply(cmd, msgno, ansno, message): |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
677 if cmd == 'RPY': |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
678 log.debug('Channel %d closed', channelno) |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
679 self.session.channels[channelno].close() |
34
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
680 if not self.session.channels: |
6da9468a6879
The build master now gracefully exits by first terminating all active sessions. Fixes #7.
cmlenz
parents:
33
diff
changeset
|
681 self.session.close() |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
682 if handle_ok is not None: |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
683 handle_ok() |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
684 elif cmd == 'ERR': |
88 | 685 elem = xmlio.parse(message.body) |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
686 text = elem.gettext() |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
687 code = int(elem.attr['code']) |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
688 log.debug('Peer refused to start channel %d: %s (%d)', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
689 channelno, text, code) |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
690 if handle_error is not None: |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
691 handle_error(code, text) |
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
692 |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
693 log.debug('Requesting closure of channel %d', channelno) |
29 | 694 xml = xmlio.Element('close', number=channelno, code=code) |
88 | 695 return self.channel.send_msg(Payload(xml), handle_reply) |
10 | 696 |
11 | 697 def send_error(self, msgno, code, message=''): |
29 | 698 """Send an error reply to the peer.""" |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
699 log.warning('%s (%d)', message, code) |
29 | 700 xml = xmlio.Element('error', code=code)[message] |
88 | 701 self.channel.send_err(msgno, Payload(xml)) |
11 | 702 |
703 def send_start(self, profiles, handle_ok=None, handle_error=None): | |
33
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
704 """Send a request to start a new channel to the peer. |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
705 |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
706 @param profiles A list of profiles to request for the channel, each |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
707 element being an instance of a `ProfileHandler` |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
708 sub-class |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
709 @param handle_ok An optional callback function that will be invoked when |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
710 the channel has been successfully started |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
711 @param handle_error An optional callback function that will be invoked |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
712 when the peer refuses to start the channel |
d8d44216258a
Exit the slave script when the master disconnects; and other minor fixes.
cmlenz
parents:
29
diff
changeset
|
713 """ |
10 | 714 channelno = self.session.channelno.next() |
53 | 715 def handle_reply(cmd, msgno, ansno, message): |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
716 if cmd == 'RPY': |
88 | 717 elem = xmlio.parse(message.body) |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
718 for cls in [p for p in profiles if p.URI == elem.attr['uri']]: |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
719 log.debug('Channel %d started with profile %s', channelno, |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
720 elem.attr['uri']) |
11 | 721 self.session.channels[channelno] = Channel(self.session, |
24
cad6d28b8975
* Proper separation between {{{beep.ProfileHandler}}} instances between different channels.
cmlenz
parents:
23
diff
changeset
|
722 channelno, cls) |
11 | 723 break |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
724 if handle_ok is not None: |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
725 handle_ok(channelno, elem.attr['uri']) |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
726 elif cmd == 'ERR': |
88 | 727 elem = xmlio.parse(message.body) |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
728 text = elem.gettext() |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
729 code = int(elem.attr['code']) |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
730 log.debug('Peer refused to start channel %d: %s (%d)', |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
731 channelno, text, code) |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
732 if handle_error is not None: |
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
733 handle_error(code, text) |
12 | 734 |
93
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
735 log.debug('Requesting start of channel %d with profiles %s', channelno, |
b289e572bc7e
Improved logging; the build master can now optionally log to a file. Closes #13.
cmlenz
parents:
92
diff
changeset
|
736 [profile.URI for profile in profiles]) |
29 | 737 xml = xmlio.Element('start', number=channelno)[ |
738 [xmlio.Element('profile', uri=profile.URI) for profile in profiles] | |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
739 ] |
88 | 740 return self.channel.send_msg(Payload(xml), handle_reply) |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
741 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
742 |
88 | 743 class Payload(object): |
744 """MIME message for transmission as payload with BEEP.""" | |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
745 |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
746 def __init__(self, data=None, content_type=BEEP_XML, |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
747 content_disposition=None, content_encoding=None): |
88 | 748 """Initialize the payload.""" |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
749 self._hdr_buf = None |
88 | 750 self.content_type = content_type |
751 self.content_disposition = content_disposition | |
752 self.content_encoding = content_encoding | |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
753 |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
754 if data is None: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
755 data = '' |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
756 if isinstance(data, xmlio.Element): |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
757 self.body = StringIO(str(data)) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
758 elif isinstance(data, (str, unicode)): |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
759 self.body = StringIO(data) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
760 else: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
761 assert hasattr(data, 'read'), \ |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
762 'Payload data %s must provide a `read` method' % data |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
763 self.body = data |
88 | 764 |
765 def as_string(self): | |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
766 return self.read() |
88 | 767 |
90
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
768 def read(self, size=None): |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
769 if self._hdr_buf is None: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
770 hdrs = [] |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
771 if self.content_type: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
772 hdrs.append('Content-Type: ' + self.content_type) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
773 if self.content_disposition: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
774 hdrs.append('Content-Disposition: ' + self.content_disposition) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
775 if self.content_encoding: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
776 hdrs.append('Content-Transfer-Encoding: ' + |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
777 self.content_encoding) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
778 hdrs.append('') |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
779 self._hdr_buf = '\n'.join(hdrs) + '\n' |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
780 |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
781 ret_buf = '' |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
782 if len(self._hdr_buf): |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
783 if size is not None and len(self._hdr_buf) > size: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
784 ret_buf = self._hdr_buf[:size] |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
785 self._hdr_buf = self._hdr_buf[size:] |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
786 return ret_buf |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
787 ret_buf = self._hdr_buf |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
788 self._hdr_buf = '' |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
789 |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
790 if not self.body.closed: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
791 ret_buf = ret_buf + self.body.read((size or -1) - len(ret_buf)) |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
792 if size is None or len(ret_buf) < size: |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
793 self.body.close() |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
794 |
2c4e104afef8
The build master now transmits snapshot archives without blocking while reading the file and sending the BEEP frames. Closes #17.
cmlenz
parents:
89
diff
changeset
|
795 return ret_buf |
88 | 796 |
797 def parse(cls, string): | |
798 message = email.message_from_string(string) | |
799 content_type = message.get('Content-Type') | |
800 content_disposition = message.get('Content-Disposition') | |
801 content_encoding = message.get('Content-Transfer-Encoding') | |
802 return Payload(message.get_payload(), content_type, | |
803 content_disposition, content_encoding) | |
804 parse = classmethod(parse) | |
14
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
805 |
1733c601d2f8
Refactored the asyncore loop and shutdown procedure into {{{beep.Initiator}}}.
cmlenz
parents:
13
diff
changeset
|
806 |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
807 def cycle_through(start, stop=None, step=1): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
808 """Utility generator that cycles through a defined range of numbers.""" |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
809 if stop is None: |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
810 stop = start |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
811 start = 0 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
812 cur = start |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
813 while True: |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
814 yield cur |
11 | 815 cur += step |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
816 if cur > stop: |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
817 cur = start |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
818 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
819 |
29 | 820 class SerialNumber(object): |
7
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
821 """Serial number (RFC 1982).""" |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
822 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
823 def __init__(self, limit=4294967295L): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
824 self.value = 0L |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
825 self.limit = limit |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
826 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
827 def __ne__(self, num): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
828 return self.value != num |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
829 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
830 def __eq__(self, num): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
831 return self.value == num |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
832 |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
833 def __iadd__(self, num): |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
834 self.value += num |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
835 if self.value > self.limit: |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
836 self.value -= self.limit |
8442bcb47a03
Initial draft of a minimal [http://www.beepcore.org/ BEEP] protocol implementation for communication between the build master and build slaves.
cmlenz
parents:
diff
changeset
|
837 return self |