Package roswtf :: Module roslaunchwtf
[frames] | no frames]

Source Code for Module roswtf.roslaunchwtf

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2009, Willow Garage, Inc. 
  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 Willow Garage, Inc. 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  # Revision $Id: roslaunchwtf.py 14270 2011-07-12 21:36:39Z kwc $ 
 34   
 35  import os 
 36  import itertools 
 37  import socket 
 38  import stat 
 39  import sys 
 40  import xmlrpclib 
 41   
 42  from os.path import isfile, isdir 
 43   
 44  import roslib.packages 
 45  import roslaunch 
 46  import roslaunch.netapi 
 47   
 48  from roswtf.environment import paths, is_executable 
 49  from roswtf.rules import warning_rule, error_rule 
 50   
51 -def bin_roslaunch_check(ctx):
52 roslaunch = os.path.join(ctx.ros_root, 'bin', 'roslaunch') 53 if not isfile(roslaunch): 54 return "%(ros_root)s/bin is missing roslaunch" 55 if not is_executable(roslaunch): 56 return "%s is lacking executable permissions"%roslaunch
57 58 # this is very similar to roslib.packages.find_node. However, we 59 # cannot use that implementation as it returns the first found 60 # path. For the sake of this test, we have to find all potential 61 # candidates.
62 -def _find_node(pkg, node_type):
63 try: 64 dir = roslib.packages.get_pkg_dir(pkg) 65 except roslib.packages.InvalidROSPkgException: 66 # caught by another rule 67 return [] 68 paths = [] 69 #UNIXONLY 70 node_exe = None 71 for p, dirs, files in os.walk(dir): 72 if node_type in files: 73 test_path = os.path.join(p, node_type) 74 s = os.stat(test_path) 75 if (s.st_mode & stat.S_IRWXU == stat.S_IRWXU): 76 paths.append(test_path) 77 if '.svn' in dirs: 78 dirs.remove('.svn') 79 elif '.git' in dirs: 80 dirs.remove('.git') 81 return paths
82 83 ## check if node is cannot be located in package
84 -def roslaunch_missing_node_check(ctx):
85 nodes = [] 86 for filename, rldeps in ctx.launch_file_deps.iteritems(): 87 nodes.extend(rldeps.nodes) 88 errors = [] 89 for pkg, node_type in nodes: 90 paths = _find_node(pkg, node_type) 91 if not paths: 92 errors.append("node [%s] in package [%s]"%(node_type, pkg)) 93 return errors
94 95 ## check if two nodes with same name in package
96 -def roslaunch_duplicate_node_check(ctx):
97 nodes = [] 98 for filename, rldeps in ctx.launch_file_deps.iteritems(): 99 nodes.extend(rldeps.nodes) 100 warnings = [] 101 for pkg, node_type in nodes: 102 paths = _find_node(pkg, node_type) 103 if len(paths) > 1: 104 warnings.append("node [%s] in package [%s]\n"%(node_type, pkg)) 105 return warnings
106
107 -def pycrypto_check(ctx):
108 try: 109 import Crypto 110 except ImportError, e: 111 return True
112
113 -def paramiko_check(ctx):
114 try: 115 import paramiko 116 except ImportError, e: 117 return True
118 -def paramiko_system_keys(ctx):
119 try: 120 import paramiko 121 ssh = paramiko.SSHClient() 122 try: 123 ssh.load_system_host_keys() #default location 124 except: 125 return True 126 except: pass
127
128 -def paramiko_ssh(ctx, address, port, username, password):
129 try: 130 import paramiko 131 ssh = paramiko.SSHClient() 132 133 import roslaunch.remoteprocess 134 err_msg = roslaunch.remoteprocess.ssh_check_known_hosts(ssh, address, port, username=username) 135 if err_msg: 136 return err_msg 137 138 if not password: #use SSH agent 139 ssh.connect(address, port, username) 140 else: #use SSH with login/pass 141 ssh.connect(address, port, username, password) 142 143 except paramiko.BadHostKeyException: 144 return "Unable to verify host key for [%s:%s]"%(address, port) 145 except paramiko.AuthenticationException: 146 return "Authentication to [%s:%s] failed"%(address, port) 147 except paramiko.SSHException, e: 148 return "[%s:%s]: %s"%(address, port, e) 149 except ImportError: 150 pass
151
152 -def _load_roslaunch_config(ctx):
153 config = roslaunch.ROSLaunchConfig() 154 loader = roslaunch.XmlLoader() 155 # TODO load roscore 156 for launch_file in ctx.launch_files: 157 loader.load(launch_file, config, verbose=False) 158 try: 159 config.validate() 160 config.assign_machines() 161 except roslaunch.RLException, e: 162 return config, [] 163 machines = [] 164 for n in itertools.chain(config.nodes, config.tests): 165 if n.machine not in machines: 166 machines.append(n.machine) 167 return config, machines
168
169 -def roslaunch_load_check(ctx):
170 config = roslaunch.ROSLaunchConfig() 171 loader = roslaunch.XmlLoader() 172 # TODO load roscore 173 for launch_file in ctx.launch_files: 174 loader.load(launch_file, config, verbose=False) 175 try: 176 config.validate() 177 config.assign_machines() 178 except roslaunch.RLException, e: 179 return str(e)
180
181 -def roslaunch_machine_name_check(ctx):
182 config, machines = _load_roslaunch_config(ctx) 183 bad = [] 184 for m in machines: 185 try: 186 socket.gethostbyname(m.address) 187 except socket.gaierror: 188 bad.append(m.address) 189 return ''.join([' * %s\n'%b for b in bad])
190
191 -def roslaunch_ssh_check(ctx):
192 import roslaunch.core 193 if not ctx.launch_files: 194 return # not relevant 195 config, machines = _load_roslaunch_config(ctx) 196 err_msgs = [] 197 for m in machines: 198 socket.setdefaulttimeout(3.) 199 # only check if the machine requires ssh to connect 200 if not roslaunch.core.is_machine_local(m): 201 err_msg = paramiko_ssh(ctx, m.address, m.ssh_port, m.user, m.password) 202 if err_msg: 203 err_msgs.append(err_msg) 204 return err_msgs
205
206 -def roslaunch_missing_pkgs_check(ctx):
207 # rospack depends does not return depends that it cannot find, so 208 # we have to manually determine this 209 config, machines = _load_roslaunch_config(ctx) 210 missing = [] 211 for n in config.nodes: 212 pkg = n.package 213 try: 214 roslib.packages.get_pkg_dir(pkg, required=True) 215 except: 216 missing.append(pkg) 217 return missing
218
219 -def roslaunch_config_errors(ctx):
220 config, machines = _load_roslaunch_config(ctx) 221 return config.config_errors
222
223 -def roslaunch_missing_deps_check(ctx):
224 missing = [] 225 for pkg, miss in ctx.launch_file_missing_deps.iteritems(): 226 if miss: 227 missing.append("%s/manifest.xml: %s"%(pkg, ', '.join(miss))) 228 return missing
229
230 -def roslaunch_respawn_check(ctx):
231 respawn = [] 232 for uri in ctx.roslaunch_uris: 233 try: 234 r = xmlrpclib.ServerProxy(uri) 235 code, msg, val = r.list_processes() 236 active, _ = val 237 respawn.extend([a for a in active if a[1] > 1]) 238 #TODO: children processes 239 #code, msg, val = r.list_children() 240 except: 241 pass # error for another rule 242 return ["%s (%s)"%(a[0], a[1]) for a in respawn]
243
244 -def roslaunch_uris_check(ctx):
245 # check for any roslaunch processes that cannot be contacted 246 bad = [] 247 # uris only contains the parent launches 248 for uri in ctx.roslaunch_uris: 249 try: 250 r = xmlrpclib.ServerProxy(uri) 251 code, msg, val = r.list_children() 252 # check the children launches 253 if code == 1: 254 for child_uri in val: 255 try: 256 r = xmlrpclib.ServerProxy(uri) 257 code, msg, val = r.get_pid() 258 except: 259 bad.append(child_uri) 260 except: 261 bad.append(uri) 262 return bad
263
264 -def roslaunch_dead_check(ctx):
265 dead = [] 266 for uri in ctx.roslaunch_uris: 267 try: 268 r = xmlrpclib.ServerProxy(uri) 269 code, msg, val = r.list_processes() 270 _, dead_list = val 271 dead.extend([d[0] for d in dead_list]) 272 #TODO: children processes 273 #code, msg, val = r.list_children() 274 except: 275 pass # error for another rule 276 return dead
277 278 online_roslaunch_warnings = [ 279 (roslaunch_respawn_check,"These nodes have respawned at least once:"), 280 (roslaunch_dead_check,"These nodes have died:"), 281 # disabling for now as roslaunches don't do cleanup 282 #(roslaunch_uris_check,"These roslaunch processes can no longer be contacted and may have exited:"), 283 ] 284 285 online_roslaunch_errors = [ 286 (roslaunch_ssh_check,"SSH failures:"), 287 ] 288 289 static_roslaunch_warnings = [ 290 (roslaunch_duplicate_node_check, "Multiple nodes of same name in packages:"), 291 (pycrypto_check, "pycrypto is not installed"), 292 (paramiko_check, "paramiko is not installed"), 293 (paramiko_system_keys, "cannot load SSH host keys -- your known_hosts file may be corrupt") , 294 (roslaunch_config_errors, "Loading your launch files reported the following configuration errors:"), 295 ] 296 static_roslaunch_errors = [ 297 (bin_roslaunch_check, "roslaunch executable is invalid:"), 298 (roslaunch_missing_deps_check, 299 "Package %(pkg)s is missing roslaunch dependencies.\nPlease add the following tags to %(pkg)s/manifest.xml:"), 300 (roslaunch_missing_pkgs_check, 301 "Cannot find the following required packages:"), 302 (roslaunch_missing_node_check, "Several nodes in your launch file could not be located. These are either typed incorrectly or need to be built:"), 303 (roslaunch_machine_name_check,"Cannot resolve the following hostnames:"), 304 (roslaunch_load_check, "roslaunch load failed"), 305 ] 306
307 -def wtf_check_static(ctx):
308 if not ctx.launch_files: 309 return 310 311 #NOTE: roslaunch files are already loaded separately into context 312 313 #TODO: check each machine name 314 #TODO: bidirectional ping for each machine 315 316 for r in static_roslaunch_warnings: 317 warning_rule(r, r[0](ctx), ctx) 318 for r in static_roslaunch_errors: 319 error_rule(r, r[0](ctx), ctx)
320
321 -def _load_online_ctx(ctx):
322 ctx.roslaunch_uris = roslaunch.netapi.get_roslaunch_uris()
323
324 -def wtf_check_online(ctx):
325 _load_online_ctx(ctx) 326 for r in online_roslaunch_warnings: 327 warning_rule(r, r[0](ctx), ctx) 328 for r in online_roslaunch_errors: 329 error_rule(r, r[0](ctx), ctx)
330