85
87
for attempt in range(attempts):
87
return client.get_juju_output('ssh', machine, cmd)
89
return client.get_juju_output('ssh', '--proxy', machine, cmd)
88
90
except subprocess.CalledProcessError as e:
89
91
# If the connection to the host failed, try again in a couple of
90
92
# seconds. This is usually due to heavy load.
91
if(attempt < attempts-1 and
92
re.search('ssh_exchange_identification: '
93
'Connection closed by remote host', e.stderr)):
93
if(attempt < attempts - 1 and
94
re.search('ssh_exchange_identification: '
95
'Connection closed by remote host', e.stderr)):
94
96
time.sleep(back_off)
242
245
result = ssh(client, source, 'more nc_listen.out')
243
246
if result.rstrip() != msg:
244
247
raise ValueError("Wrong or missing message: %r" % result.rstrip())
247
251
def private_address(client, host):
248
252
default_route = ssh(client, host, 'ip -4 -o route list 0/0')
249
device = re.search(r'(\w+)\s*$', default_route).group(1)
253
log.info("Default route from {}: {}".format(host, default_route))
254
route_match = re.search(r'([\w-]+)\s*$', default_route)
255
if route_match is None:
256
raise JujuAssertionError(
257
"Failed to find device in {}".format(default_route))
258
device = route_match.group(1)
259
log.info("Fetching the device IP of {}".format(device))
250
260
device_ip = ssh(client, host, 'ip -4 -o addr show {}'.format(device))
251
return re.search(r'inet\s+(\S+)/\d+\s', device_ip).group(1)
261
log.info("Device IP for {}: {}".format(host, device_ip))
262
ip_match = re.search(r'inet\s+(\S+)/\d+\s', device_ip)
264
raise JujuAssertionError(
265
"Failed to find ip for device: {}".format(device))
266
return ip_match.group(1)
254
269
def assess_address_range(client, targets):
285
303
:param targets: machine IDs of machines to test
286
304
:return: None; raises ValueError on failure
306
log.info('Assessing internet connection.')
289
307
for target in targets:
308
log.info("Assessing internet connection for {}".format(target))
290
309
routes = ssh(client, target, 'ip route show')
292
311
d = re.search(r'^default\s+via\s+([\d\.]+)\s+', routes, re.MULTILINE)
294
rc = client.juju('ssh', (target, 'ping -c1 -q ' + d.group(1)),
313
rc = client.juju('ssh', ('--proxy', target,
314
'ping -c1 -q ' + d.group(1)), check=False)
297
316
raise ValueError('%s unable to ping default route' % target)
299
318
raise ValueError("Default route not found")
302
322
def _assessment_iteration(client, containers):
325
345
test_containers = [
326
346
containers[container_type][hosts[0]][0],
327
347
containers[container_type][hosts[1]][0],
329
349
_assessment_iteration(client, test_containers)
331
351
if KVM_MACHINE in types and LXC_MACHINE in types:
332
352
test_containers = [
333
353
containers[LXC_MACHINE][hosts[0]][0],
334
354
containers[KVM_MACHINE][hosts[0]][0],
336
356
_assessment_iteration(client, test_containers)
338
358
# Test with an LXC and a KVM on different machines
339
359
test_containers = [
340
360
containers[LXC_MACHINE][hosts[0]][0],
341
361
containers[KVM_MACHINE][hosts[1]][0],
343
363
_assessment_iteration(client, test_containers)
366
def get_uptime(client, host):
367
uptime_pattern = re.compile(r'.*(\d+)')
368
uptime_output = ssh(client, host, 'uptime -p')
369
log.info('uptime -p: {}'.format(uptime_output))
370
match = uptime_pattern.match(uptime_output)
372
return int(match.group(1))
346
377
def assess_container_networking(client, types):
347
378
"""Runs _assess_address_allocation, reboots hosts, repeat.
348
380
:param client: Juju client
349
381
:param types: Container types to test
384
log.info("Setting up test.")
352
385
hosts, containers = make_machines(client, types)
353
386
status = client.wait_for_started().status
387
log.info("Setup complete.")
388
log.info("Test started.")
355
390
_assess_container_networking(client, types, hosts, containers)
357
# Reboot all hosts apart from machine 0 because we use machine 0 to jump
358
# through for some hosts.
359
for host in hosts[1:]:
360
ssh(client, host, 'sudo reboot')
362
# Finally reboot machine 0
363
ssh(client, hosts[0], 'sudo reboot')
365
# Wait for the state server to shut down. This prevents us from calling
366
# wait_for_started before machine 0 has shut down, which can cause us
367
# to think that we have finished rebooting before we actually have.
368
hostname = status['machines'][hosts[0]]['dns-name']
369
wait_for_port(hostname, 22, closed=True)
392
# Reboot all hosted modelled machines then the controller.
393
log.info("Instrumenting reboot of all machines.")
396
log.info("Restarting hosted machine: {}".format(host))
398
'run', ('--machine', host, 'sudo shutdown -r now'))
399
client.juju('show-action-status', ('--name', 'juju-run'))
401
log.info("Restarting controller machine 0")
402
controller_client = client.get_controller_client()
403
controller_status = controller_client.get_status()
404
controller_host = controller_status.status['machines']['0']['dns-name']
405
first_uptime = get_uptime(controller_client, '0')
406
ssh(controller_client, '0', 'sudo shutdown -r now')
407
except subprocess.CalledProcessError as e:
409
"Error running shutdown:\nstdout: %s\nstderr: %s",
410
e.output, getattr(e, 'stderr', None))
413
# Wait for the controller to shut down if it has not yet restarted.
414
# This ensure the call to wait_for_started happens after each host
416
second_uptime = get_uptime(controller_client, '0')
417
if second_uptime > first_uptime:
418
wait_for_port(controller_host, 22, closed=True, timeout=300)
371
419
client.wait_for_started()
373
421
# Once Juju is up it can take a little while before ssh responds.
374
422
for host in hosts:
375
423
hostname = status['machines'][host]['dns-name']
376
424
wait_for_port(hostname, 22, timeout=240)
425
log.info("Reboot complete and all hosts ready for retest.")
378
427
_assess_container_networking(client, types, hosts, containers)
381
431
class _CleanedContext: