~nuclearbob/ubuntu-test-cases/bootspeed-nfss

« back to all changes in this revision

Viewing changes to scripts/recover.py

MergedĀ upstreamĀ changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
import device_info
4
4
import logging
5
 
import subprocess
 
5
import os
 
6
import requests
6
7
import sys
7
8
import time
8
 
from ncd_usb import set_relay
 
9
import yaml
9
10
 
10
11
log = logging.getLogger()
11
12
logging.basicConfig(level=logging.INFO)
12
13
 
13
14
 
14
 
class DeviceError(Exception):
15
 
    pass
16
 
 
17
 
 
18
 
def _reimage_from_fastboot(serial):
19
 
    #Starting from fastboot mode, put a known-good image on the device
20
 
    log.info("Flashing the last stable image")
21
 
    subprocess.check_output(['ubuntu-device-flash', '--serial', serial,
22
 
                             '--channel', 'ubuntu-touch/stable',
23
 
                             '--bootstrap', '--password', 'ubuntuci'])
24
 
    return _wait_for_device(serial, 600)
25
 
 
26
 
 
27
 
def _wait_for_device(serial, timeout=120):
28
 
    # Wait for the device to come up to a good/booted state
29
 
    log.info("Waiting for the device to become available")
 
15
def _full_recovery(device):
30
16
    try:
31
 
        subprocess.check_call(['timeout', str(timeout), 'adb', '-s',
32
 
                               serial, 'wait-for-device'])
 
17
        device.force_bootloader()
33
18
    except:
34
 
        log.error("Timed out waiting for reboot. Recover device manually")
35
 
        raise
36
 
    dev_state = device_info.get_state(serial)
37
 
    if dev_state != 'device':
38
 
        raise DeviceError("Device in state: {0}, still not available after "
39
 
                          "{1} seconds".format(dev_state, timeout))
40
 
    else:
41
 
        log.info("Device is now available")
42
 
        return 0
43
 
 
44
 
 
45
 
def _wait_for_fastboot(serial, timeout=120):
46
 
    if timeout > 10:
47
 
        wait = 10
48
 
    else:
49
 
        wait = timeout
50
 
    waited = 0
51
 
    while waited < timeout:
52
 
        state = device_info.get_state(serial)
53
 
        if state == 'fastboot':
54
 
            return 0
55
 
        time.sleep(wait)
56
 
        waited += wait
57
 
    else:
58
 
        state = device_info.get_state(serial)
59
 
        if state == 'fastboot':
60
 
            return 0
61
 
        log.error("Timed out waiting for fastboot. Recover device manually")
62
 
        raise DeviceError("Device in state: {0}, still not available after "
63
 
                          "{1} seconds".format(state, timeout))
64
 
 
65
 
 
66
 
def _mako_to_bootloader(urlbase, bank, power=1, volume=2):
67
 
    """
68
 
    This just works on mako for certain, but that's all we have connected
69
 
    right now. After this runs, the device should be in the bootloader
70
 
    """
71
 
    log.info("Forcing the device to enter the bootloader")
72
 
    #Power off the device from any state
73
 
    set_relay(urlbase, bank, power, 1)
74
 
    time.sleep(10)
75
 
    set_relay(urlbase, bank, power, 0)
76
 
    time.sleep(10)
77
 
    #Enter the bootloader
78
 
    set_relay(urlbase, bank, volume, 1)
79
 
    set_relay(urlbase, bank, power, 1)
80
 
    time.sleep(5)
81
 
    set_relay(urlbase, bank, volume, 0)
82
 
    set_relay(urlbase, bank, power, 0)
83
 
 
84
 
 
85
 
def _full_recovery(device_name):
86
 
    #we only support mako at the moment
87
 
    (url, bank, power, volume) = device_info.get_power(device_name)
88
 
    if None in (url, bank, power, volume):
89
19
        #This device does not have information about relays
90
 
        raise DeviceError("Full recovery not possible with this device")
91
 
    _mako_to_bootloader(url, bank, power, volume)
92
 
    serial = device_info.get_serial(device_name)
93
 
    _wait_for_fastboot(serial)
94
 
    _reimage_from_fastboot(serial)
95
 
    try:
96
 
        _check_adb_shell(serial)
 
20
        _offline_device()
 
21
        raise
 
22
    try:
 
23
        device.wait_for_fastboot()
 
24
        device.reimage_from_fastboot()
 
25
    except:
 
26
        _offline_device()
 
27
        raise
 
28
    try:
 
29
        device.check_adb_shell()
97
30
    except:
98
31
        # The device looks like it's available, but not responding
99
 
        raise DeviceError("Could not fully recover {}".format(device_name))
 
32
        _offline_device()
 
33
        raise device_info.DeviceError("Could not fully recover device")
100
34
    return 0
101
35
 
102
36
 
103
 
def _check_adb_shell(serial):
104
 
    # Run a quick command in adb to see if the device is responding
105
 
    subprocess.check_call(['timeout', '10', 'adb', '-s',
106
 
                           serial, 'shell', 'pwd'])
107
 
 
108
 
 
109
 
def recover(device):
110
 
    try:
111
 
        serial = device_info.get_serial(device)
112
 
    except AttributeError:
113
 
        log.error("No device found for '{}'".format(device))
 
37
def _get_jenkins_creds(url):
 
38
    try:
 
39
        home = os.environ.get('HOME')
 
40
        credpath = os.path.join(home, '.ubuntu-ci/jenkins-keys.yaml')
 
41
        with open(credpath) as credfile:
 
42
            creds = yaml.load(credfile.read())
 
43
        jenkins = creds.get(url)
 
44
        user = jenkins.get('user')
 
45
        key = jenkins.get('key')
 
46
    except (AttributeError, IOError):
 
47
        user = None
 
48
        key = None
 
49
    return (user, key)
 
50
 
 
51
 
 
52
def _offline_device():
 
53
    # It's unlikely the node name will be different, but just in case
 
54
    node = os.environ.get('NODE_NAME', None)
 
55
    host = os.environ.get('JENKINS_URL', None)
 
56
    (user, key) = _get_jenkins_creds(host)
 
57
    if not (user and key and host and node):
 
58
        log.warn("Unable to mark device offline automatically")
 
59
        return
 
60
    url = "{}/computer/{}/toggleOffline".format(host, node)
 
61
    param_data = {'offlineMessage': 'unrecoverable'}
 
62
    delay = 2
 
63
    # Retry with exponential delay from 1 to 128 seconds
 
64
    # This will retry for no longer than 4 min 15 sec before failing
 
65
    for attempt in range(8):
 
66
        time.sleep(delay ** attempt)
 
67
        try:
 
68
            response = requests.post(url, params=param_data, auth=(user, key))
 
69
        except Exception as exc:
 
70
            log.exception('Error contacting jenkins: {}'.format(exc.message))
 
71
            continue
 
72
        if response.status_code != 200:
 
73
            log.warn("Error marking {} offline, retrying".format(node))
 
74
        else:
 
75
            log.info("{} has been marked offline".format(node))
 
76
            return
 
77
    log.error("Fatal error marking {} offline".format(node))
 
78
 
 
79
 
 
80
def recover(device_name):
 
81
    try:
 
82
        device = device_info.get_device(device_name)
 
83
    except KeyError:
 
84
        log.error("No device found for '{}'".format(device_name))
114
85
        raise
115
 
    state = device_info.get_state(serial)
 
86
    state = device.get_state()
116
87
    if state in ('device', 'recovery'):
117
88
        try:
118
 
            _check_adb_shell(serial)
 
89
            device.check_adb_shell()
119
90
        except:
120
91
            # The device looks like it's available, but not responding
121
92
            return _full_recovery(device)
123
94
        return 0
124
95
    if state == 'fastboot':
125
96
        #The device is in fastboot right now, we need it booted first
126
 
        return _reimage_from_fastboot(serial)
127
97
        try:
128
 
            _check_adb_shell(serial)
 
98
            device.reimage_from_fastboot()
 
99
            device.check_adb_shell()
 
100
            return 0
129
101
        except:
130
102
            # The device looks like it's available, but not responding
131
103
            return _full_recovery(device)
133
105
        #The device is in an unknown state, we need full recovery
134
106
        return _full_recovery(device)
135
107
    #In theory, we should never get here, but....
136
 
    raise DeviceError("Device '{}' is in an unknown state!".format(device))
 
108
    _offline_device()
 
109
    raise device_info.DeviceError(
 
110
        "Device '{}' is in an unknown state!".format(device_name))
137
111
 
138
112
 
139
113
if __name__ == '__main__':