Package conduit :: Package modules :: Package NetworkModule :: Module Peers
[hide private]

Source Code for Module conduit.modules.NetworkModule.Peers

  1  """ 
  2  Contains classes for advertising conduit via avahi and for transmitting and 
  3  receiving python objects over the network. 
  4   
  5  Parts of this code adapted from glchess (GPLv2) 
  6  http://glchess.sourceforge.net/ 
  7  Parts of this code adapted from elisa (GPLv2) 
  8  Parts of this code adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/457669 
  9   
 10  Copyright: John Stowers, 2006 
 11  License: GPLv2 
 12  """ 
 13   
 14  import dbus.glib 
 15  import logging 
 16  log = logging.getLogger("modules.Network") 
 17   
 18  import conduit 
 19   
 20  AVAHI_SERVICE_NAME = "_conduit._tcp" 
 21  AVAHI_SERVICE_DOMAIN = "" 
 22  PROTOCOL_VERSION = "1" 
 23   
 24  PORT_IDX = 0 
 25  VERSION_IDX = 1 
 26   
 27  ### 
 28  #Instead of having to depend on python-avahi we just  
 29  #copy the functions and constants we need 
 30  ### 
 31  DBUS_INTERFACE_ADDRESS_RESOLVER = 'org.freedesktop.Avahi.AddressResolver' 
 32  DBUS_INTERFACE_DOMAIN_BROWSER = 'org.freedesktop.Avahi.DomainBrowser' 
 33  DBUS_INTERFACE_ENTRY_GROUP = 'org.freedesktop.Avahi.EntryGroup' 
 34  DBUS_INTERFACE_HOST_NAME_RESOLVER = 'org.freedesktop.Avahi.HostNameResolver' 
 35  DBUS_INTERFACE_RECORD_BROWSER = 'org.freedesktop.Avahi.RecordBrowser' 
 36  DBUS_INTERFACE_SERVER = 'org.freedesktop.Avahi.Server' 
 37  DBUS_INTERFACE_SERVICE_BROWSER = 'org.freedesktop.Avahi.ServiceBrowser' 
 38  DBUS_INTERFACE_SERVICE_RESOLVER = 'org.freedesktop.Avahi.ServiceResolver' 
 39  DBUS_INTERFACE_SERVICE_TYPE_BROWSER = 'org.freedesktop.Avahi.ServiceTypeBrowser' 
 40  DBUS_NAME = 'org.freedesktop.Avahi' 
 41  DBUS_PATH_SERVER = '/' 
 42  DOMAIN_BROWSER_BROWSE = 0 
 43  DOMAIN_BROWSER_BROWSE_DEFAULT = 1 
 44  DOMAIN_BROWSER_BROWSE_LEGACY = 4 
 45  DOMAIN_BROWSER_REGISTER = 2 
 46  DOMAIN_BROWSER_REGISTER_DEFAULT = 3 
 47  ENTRY_GROUP_COLLISION = 3 
 48  ENTRY_GROUP_ESTABLISHED = 2 
 49  ENTRY_GROUP_FAILURE = 4 
 50  ENTRY_GROUP_REGISTERING = 1 
 51  ENTRY_GROUP_UNCOMMITED = 0 
 52  IF_UNSPEC = -1 
 53  LOOKUP_NO_ADDRESS = 8 
 54  LOOKUP_NO_TXT = 4 
 55  LOOKUP_RESULT_CACHED = 1 
 56  LOOKUP_RESULT_LOCAL = 8 
 57  LOOKUP_RESULT_MULTICAST = 4 
 58  LOOKUP_RESULT_OUR_OWN = 16 
 59  LOOKUP_RESULT_STATIC = 32 
 60  LOOKUP_RESULT_WIDE_AREA = 2 
 61  LOOKUP_USE_MULTICAST = 2 
 62  LOOKUP_USE_WIDE_AREA = 1 
 63  PROTO_INET = 0 
 64  PROTO_INET6 = 1 
 65  PROTO_UNSPEC = -1 
 66  PUBLISH_ALLOW_MULTIPLE = 8 
 67  PUBLISH_NO_ANNOUNCE = 4 
 68  PUBLISH_NO_COOKIE = 32 
 69  PUBLISH_NO_PROBE = 2 
 70  PUBLISH_NO_REVERSE = 16 
 71  PUBLISH_UNIQUE = 1 
 72  PUBLISH_UPDATE = 64 
 73  PUBLISH_USE_MULTICAST = 256 
 74  PUBLISH_USE_WIDE_AREA = 128 
 75  SERVER_COLLISION = 3 
 76  SERVER_FAILURE = 4 
 77  SERVER_INVALID = 0 
 78  SERVER_REGISTERING = 1 
 79  SERVER_RUNNING = 2 
 80  SERVICE_COOKIE = 'org.freedesktop.Avahi.cookie' 
 81  SERVICE_COOKIE_INVALID = 0 
 82   
83 -def byte_array_to_string(s):
84 r = "" 85 for c in s: 86 if c >= 32 and c < 127: 87 r += "%c" % c 88 else: 89 r += "." 90 return r
91
92 -def txt_array_to_string_array(t):
93 l = [] 94 for s in t: 95 l.append(byte_array_to_string(s)) 96 return l
97 98
99 -def string_to_byte_array(s):
100 r = [] 101 for c in s: 102 r.append(dbus.Byte(ord(c))) 103 return r
104
105 -def string_array_to_txt_array(t):
106 l = [] 107 for s in t: 108 l.append(string_to_byte_array(s)) 109 return l
110
111 -def dict_to_txt_array(txt_dict):
112 l = [] 113 for k,v in txt_dict.items(): 114 l.append(string_to_byte_array("%s=%s" % (k,v))) 115 return l
116
117 -def txt_array_to_dict(array):
118 d = {} 119 for i in array: 120 bits = i.split("=") 121 if len(bits) == 2: 122 d[bits[0]] = bits[1] 123 return d
124
125 -class AvahiAdvertiser:
126 """ 127 Advertises the presence of dataprovider instances on the network using avahi. 128 Wraps up some of the complexity due to it being hard to add additional 129 services to a group once that group has been committed. 130 """
131 - def __init__(self, name, port):
132 self.name = name 133 self.port = port 134 135 # Connect to the Avahi server 136 bus = dbus.SystemBus() 137 server = dbus.Interface( 138 bus.get_object( 139 DBUS_NAME, 140 DBUS_PATH_SERVER 141 ), 142 DBUS_INTERFACE_SERVER 143 ) 144 145 # Get this device's hostname 146 self.hostname = server.GetHostName() 147 148 # Register this service 149 path = server.EntryGroupNew() 150 self.group = dbus.Interface( 151 bus.get_object(DBUS_NAME, path), 152 DBUS_INTERFACE_ENTRY_GROUP 153 )
154
155 - def announce(self):
156 """ 157 Resets the group, announces Conduit, and commits the change 158 """ 159 log.debug("Announcing avahi conduit service") 160 self.group.AddService( 161 IF_UNSPEC, #interface 162 PROTO_UNSPEC, #protocol 163 dbus.UInt32(0), #flags 164 self.hostname, #name 165 AVAHI_SERVICE_NAME, #service type 166 AVAHI_SERVICE_DOMAIN, #domain 167 '', #host 168 dbus.UInt16(self.port), #port 169 string_array_to_txt_array([ 170 "version=%s" % conduit.VERSION, 171 "protocol-version=%s" % PROTOCOL_VERSION]) 172 ) 173 self.group.Commit()
174
175 - def reset(self):
176 if not self.group.IsEmpty(): 177 self.group.Reset()
178
179 -class AvahiMonitor:
180 """ 181 Watches the network for other conduit instances using avahi. 182 183 Code adapted from elisa 184 """
185 - def __init__(self, dataprovider_detected_cb, dataprovider_removed_cb):
186 """ 187 Connects to the system bus and configures avahi to listen for 188 Conduit services 189 """ 190 #Callbacks fired when a conduit dataprovider is detected 191 self.detected_cb = dataprovider_detected_cb 192 self.removed_cb = dataprovider_removed_cb 193 194 bus = dbus.SystemBus() 195 self.server = dbus.Interface( 196 bus.get_object( 197 DBUS_NAME, 198 DBUS_PATH_SERVER), 199 DBUS_INTERFACE_SERVER) 200 201 self.hostname = self.server.GetHostName() 202 203 obj = bus.get_object( 204 DBUS_NAME, 205 self.server.ServiceBrowserNew( 206 IF_UNSPEC, 207 PROTO_UNSPEC, 208 AVAHI_SERVICE_NAME, 209 AVAHI_SERVICE_DOMAIN, 210 dbus.UInt32(0) 211 ) 212 ) 213 browser = dbus.Interface(obj, DBUS_INTERFACE_SERVICE_BROWSER) 214 browser.connect_to_signal('ItemNew', self._new_service) 215 browser.connect_to_signal('ItemRemove', self._remove_service)
216
217 - def _new_service(self, interface, protocol, name, type, domain, flags):
218 """ 219 DBus callback when a new service is detected 220 """ 221 #Dont show networked dataproviders on localhost unless we are 222 #a development release 223 if not conduit.IS_DEVELOPMENT_VERSION and flags & LOOKUP_RESULT_OUR_OWN: 224 return 225 226 service = self.server.ResolveService( 227 interface, 228 protocol, 229 name, 230 type, 231 domain, 232 PROTO_UNSPEC, 233 dbus.UInt32(0), 234 reply_handler = self._resolve_service, 235 error_handler = self._resolve_error 236 )
237
238 - def _resolve_service(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
239 """ 240 Dbus callback 241 """ 242 extra_info = txt_array_to_string_array(txt) 243 extra = txt_array_to_dict(extra_info) 244 245 log.debug("Resolved conduit service %s on %s - %s:%s\nExtra Info: %s" % (name, host, address, port, extra_info)) 246 247 # Check if the service is local and then check the 248 # conduit versions are identical 249 if extra.get("protocol-version", None) == PROTOCOL_VERSION: 250 self.detected_cb(str(name), str(host), str(address), str(port), extra_info) 251 else: 252 log.debug("Ignoring %s (version: %s, protocol version: %s)" % ( 253 name, 254 extra.get("version", "unknown"), 255 extra.get("protocol-version", "unknown")))
256
257 - def _remove_service(self, interface, protocol, name, type, domain, flags):
258 """ 259 Dbus callback when a service is removed 260 """ 261 self.removed_cb(str(name))
262
263 - def _resolve_error(self, error):
264 """ 265 Dbus callback when a service details cannot be resolved 266 """ 267 log.warn("Avahi/D-Bus error: %s" % repr(error))
268