Snapshot per-hop loss/latency from this host to `target` over `count` probe cycles. `source`, if given, binds the trace to one of this host's local IPv4 addresses (mtr -a) so a multi-homed box can choose which interface/path the probes leave from.
(target, count=5, source=None)
| 135 | summary['transmitted'] = int(m.group(1)) |
| 136 | summary['received'] = int(m.group(2)) |
| 137 | summary['loss_pct'] = float(m.group(3)) |
| 138 | m = re.search(r'=\s*([\d.]+)/([\d.]+)/([\d.]+)/([\d.]+)\s*ms', out) |
| 139 | if m: |
| 140 | summary['rtt_min'] = float(m.group(1)) |
| 141 | summary['rtt_avg'] = float(m.group(2)) |
| 142 | summary['rtt_max'] = float(m.group(3)) |
| 143 | # ping returns non-zero on 100% loss; treat "ran" as success and let the |
| 144 | # summary/output convey the result. |
| 145 | if res['rc'] == 127: |
| 146 | return {'success': False, 'output': '', 'summary': {}, |
| 147 | 'error': 'ping is not installed. Click Install to add it.', |
| 148 | 'missing_tool': 'ping'} |
| 149 | return {'success': True, 'output': out.strip(), 'summary': summary, 'error': None} |
| 150 | |
| 151 | |
| 152 | def do_traceroute(target, max_hops=20): |
| 153 | max_hops = _clamp_int(max_hops, 20, 1, 30) |
| 154 | res = _run(['traceroute', '-n', '-q', '1', '-w', '2', '-m', str(max_hops), target], |
| 155 | timeout=max_hops * 3 + 10) |
| 156 | if res['rc'] == 127: |
| 157 | return {'success': False, 'output': '', |
| 158 | 'error': 'traceroute is not installed. Click Install to add it.', |
| 159 | 'missing_tool': 'traceroute'} |
| 160 | return {'success': True, 'output': (res['out'] or res['err']).strip(), 'error': None} |
| 161 | |
| 162 | |
| 163 | def _local_ipv4_addrs(): |
| 164 | """Set of this host's IPv4 addresses (no CIDR), across all interfaces. |
| 165 | Used to validate an mtr source ('start point') binding.""" |
| 166 | addrs = set() |
| 167 | for name in _list_iface_names(include_virtual=True): |
| 168 | v4, _ = _iface_addrs(name) |
| 169 | for a in v4: |
| 170 | addrs.add(a.split('/')[0]) |
| 171 | return addrs |
| 172 | |
| 173 | |
| 174 | def do_mtr(target, count=5, source=None): |
| 175 | """Snapshot per-hop loss/latency from this host to `target` over `count` |
| 176 | probe cycles. `source`, if given, binds the trace to one of this host's |
| 177 | local IPv4 addresses (mtr -a) so a multi-homed box can choose which |
| 178 | interface/path the probes leave from.""" |
| 179 | count = _clamp_int(count, 5, 1, 20) |
| 180 | cmd = ['mtr', '-n', '-r', '-c', str(count)] |
| 181 | if source: |
| 182 | try: |
| 183 | ipaddress.ip_address(source) |
no test coverage detected