Package node_manager_fkie :: Module name_resolution
[frames] | no frames]

Source Code for Module node_manager_fkie.name_resolution

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from threading import Thread, RLock 
 34  from urlparse import urlparse 
 35  import socket 
 36   
 37  import rospy 
 38  from master_discovery_fkie.common import get_hostname 
 39  from node_manager_fkie.common import utf8 
 40   
 41  RESOLVE_CACHE = {}  # hostname : address 
42 43 44 -class MasterEntry(object):
45
46 - def __init__(self, masteruri=None, mastername=None, address=None):
47 self.masteruri = masteruri 48 self._masternames = [] 49 self.add_mastername(mastername) 50 self.mutex = RLock() 51 # addresses: hostname (at first place if available), IPv4 or IPv6 52 self._addresses = [] 53 self.add_address(address)
54
55 - def __repr__(self):
56 return ''.join([utf8(self.masteruri), ':\n', 57 ' masternames: ', utf8(self._masternames), '\n', 58 ' addresses: ', utf8(self._addresses), '\n'])
59
60 - def entry(self):
61 return (self.masteruri, list(self._masternames), list(self._addresses))
62
63 - def has_mastername(self, mastername):
64 return mastername in self._masternames
65
66 - def has_address(self, address):
67 with self.mutex: 68 return address in self._addresses
69
70 - def add_mastername(self, mastername):
71 if mastername and mastername not in self._masternames: 72 self._masternames.append(mastername)
73
74 - def add_address(self, address):
75 if address and not self.has_address(address): 76 if self.is_legal_ip(address): 77 # it is an IP, try to get the hostname 78 with self.mutex: 79 self._addresses.append(address) 80 # resolve the name in a thread 81 thread = Thread(target=self._get_hostname, args=((address,))) 82 thread.daemon = True 83 thread.start() 84 else: 85 # it is a hostname: add at the fist place and try to get an IP for this host 86 with self.mutex: 87 self._addresses.insert(0, address) 88 # resolve the name in a thread 89 thread = Thread(target=self._get_address, args=((address,))) 90 thread.daemon = True 91 thread.start()
92 93 @classmethod 110
111 - def _get_address(self, hostname):
112 try: 113 (_, _, ipaddrlist) = socket.gethostbyaddr(hostname) 114 with self.mutex: 115 if ipaddrlist: 116 RESOLVE_CACHE[hostname] = ipaddrlist 117 for addr in ipaddrlist: 118 if not self.has_address(addr): 119 self._addresses.append(addr) 120 except socket.gaierror: 121 # no suitable address found 122 pass
123
124 - def _get_hostname(self, address):
125 try: 126 (hostname, _, _) = socket.gethostbyaddr(address) 127 with self.mutex: 128 name_splitted = hostname.split('.') 129 RESOLVE_CACHE[address] = [name_splitted[0], hostname] 130 if not self.has_address(hostname): 131 self._addresses.insert(0, hostname) 132 if not self.has_address(name_splitted[0]): 133 self._addresses.insert(0, name_splitted[0]) 134 except socket.gaierror: 135 # no suitable address found 136 pass
137
138 - def get_mastername(self):
139 try: 140 return self._masternames[0] 141 except: 142 return None
143
144 - def get_masternames(self):
145 return list(self._masternames)
146
147 - def get_address(self):
148 with self.mutex: 149 try: 150 return self._addresses[0] 151 except: 152 return None
153
154 - def addresses(self):
155 return list(self._addresses)
156
157 - def remove_mastername(self, mastername):
158 try: 159 self._masternames.remove(mastername) 160 except: 161 pass
162
163 - def remove_address(self, address):
164 try: 165 self._addresses.remove(address) 166 except: 167 pass
168
169 170 -class NameResolution(object):
171 ''' 172 This class stores the association between master URI, master name and 173 host name or IP. Both the setter and the getter methods are thread safe. 174 ''' 175
176 - def __init__(self):
177 self.mutex = RLock() 178 self._masters = [] # sets with masters 179 self._hosts = [] # sets with hosts 180 self._address = [] # avoid the mixing of ip and name as address
181
182 - def remove_master_entry(self, masteruri):
183 with self.mutex: 184 for m in self._masters: 185 if masteruri and m.masteruri == masteruri: 186 self._masters.remove(m) 187 return
188
189 - def remove_info(self, mastername, address):
190 with self.mutex: 191 for m in self._masters: 192 if m.has_mastername(mastername) and m.has_address(address): 193 m.remove_mastername(mastername) 194 m.remove_address(address) 195 return
196
197 - def add_master_entry(self, masteruri, mastername, address):
198 with self.mutex: 199 mastername = self._validate_mastername(mastername, masteruri) 200 for m in self._masters: 201 if m.masteruri and m.masteruri == masteruri: 202 m.add_mastername(mastername) 203 m.add_address(address) 204 return 205 elif m.masteruri is None and m.has_mastername(mastername): 206 m.masteruri = masteruri 207 m.add_mastername(mastername) 208 m.add_address(address) 209 return 210 self._masters.append(MasterEntry(masteruri, mastername, address))
211
212 - def add_info(self, mastername, address):
213 with self.mutex: 214 for m in self._masters: 215 if m.has_mastername(mastername): 216 m.add_mastername(mastername) 217 m.add_address(address) 218 return 219 if mastername is not None: 220 self._masters.append(MasterEntry(None, mastername, address))
221
222 - def _validate_mastername(self, mastername, masteruri):
223 ''' 224 Not thread safe 225 ''' 226 mm = self.masteruri(mastername) 227 if mm and mm != masteruri: 228 nr = 2 229 new_name = '%s_%d' % (mastername, nr) 230 mm = self.masteruri(new_name) 231 while mm and mm != masteruri: 232 nr = nr + 1 233 new_name = '%s_%d' % (mastername, nr) 234 mm = self.masteruri(new_name) 235 rospy.logwarn("master name '%s' is already assigned to '%s', rename to '%s'" % (mastername, mm, new_name)) 236 return new_name 237 return mastername
238
239 - def has_master(self, masteruri):
240 with self.mutex: 241 for m in self._masters: 242 if m.masteruri == masteruri: 243 return True 244 return False
245
246 - def mastername(self, masteruri, address=None):
247 with self.mutex: 248 for m in self._masters: 249 if m.masteruri == masteruri: 250 if address is not None: 251 if m.has_address(address): 252 return m.get_mastername() 253 else: 254 return m.get_mastername() 255 return get_hostname(masteruri)
256
257 - def masternames(self, masteruri):
258 with self.mutex: 259 for m in self._masters: 260 if m.masteruri == masteruri: 261 return m.get_masternames() 262 return list()
263
264 - def masternamebyaddr(self, address):
265 with self.mutex: 266 for m in self._masters: 267 if m.has_address(address): 268 return m.get_mastername() 269 return None
270
271 - def masteruri(self, mastername):
272 with self.mutex: 273 for m in self._masters: 274 if m.has_mastername(mastername): 275 return m.masteruri 276 return None
277
278 - def masterurisbyaddr(self, address):
279 with self.mutex: 280 result = [] 281 for m in self._masters: 282 if m.has_address(address) and m.masteruri and m.masteruri not in result: 283 result.append(m.masteruri) 284 return result
285
286 - def address(self, masteruri):
287 with self.mutex: 288 for m in self._masters: 289 if m.masteruri == masteruri or m.has_mastername(masteruri): 290 return m.get_address() 291 return get_hostname(masteruri)
292
293 - def addresses(self, masteruri):
294 with self.mutex: 295 for m in self._masters: 296 if m.masteruri == masteruri or m.has_mastername(masteruri): 297 return m.addresses() 298 return []
299
300 - def hostname(self, address, resolve=False):
301 with self.mutex: 302 for m in self._masters: 303 if m.has_address(address) or m.has_mastername(address): 304 result = m.get_address() 305 if result and not MasterEntry.is_legal_ip(result): 306 return result 307 else: 308 break 309 try: 310 if MasterEntry.is_legal_ip(address): 311 (hostname, _, _) = socket.gethostbyaddr(address) 312 return hostname 313 except: 314 import traceback 315 print traceback.format_exc() 316 return address
317 318 @classmethod
319 - def masteruri2name(cls, masteruri):
320 result = masteruri 321 try: 322 url = urlparse(masteruri) 323 if url.port == 11311: 324 result = '%s' % url.hostname 325 else: 326 result = '%s_%d' % (url.hostname, url.port) 327 except: 328 pass 329 return cls.normalize_name(result)
330 331 @classmethod
332 - def normalize_name(cls, name):
333 result = name.replace('-', '_').replace('.', '_') 334 return result
335 336 @classmethod 339
340 - def resolve_cached(self, hostname):
341 try: 342 return RESOLVE_CACHE[hostname] 343 except: 344 pass 345 return [hostname]
346 347 @classmethod
348 - def get_ros_hostname(cls, url):
349 ''' 350 Returns the host name used in a url, if it is a name. If it is an IP an 351 empty string will be returned. 352 353 @return: host or '' if url is an IP or invalid 354 @rtype: C{str} 355 ''' 356 hostname = get_hostname(url) 357 if hostname is not None: 358 if hostname != 'localhost': 359 if '.' not in hostname and ':' not in hostname: 360 local_hostname = 'localhost' 361 try: 362 # ROS resolves the 'localhost' to local hostname 363 local_hostname = socket.gethostname() 364 except: 365 pass 366 if hostname != local_hostname: 367 return hostname 368 return ''
369