liyujie
2025-08-28 b3810562527858a3b3d98ffa6e9c9c5b0f4a9a8e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/python
 
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A small wrapper script, iterates through
the known hosts and tries to call get_labels()
to discover host functionality, and adds these
detected labels to host.
 
Limitations:
 - Does not keep a count of how many labels were
   actually added.
 - If a label is added by this script because it
   is detected as supported by get_labels, but later becomes
   unsupported, this script has no way to know that it
   should be removed, so it will remain attached to the host.
   See crosbug.com/38569
"""
 
 
from multiprocessing import pool
import logging
import socket
import argparse
import sys
 
import common
 
from autotest_lib.server import hosts
from autotest_lib.server import frontend
from autotest_lib.client.common_lib import error
 
 
# A list of label prefix that each dut should only have one of such label with
# the given prefix, e.g., a dut can't have both labels of power:battery and
# power:AC_only.
SINGLETON_LABEL_PREFIX = ['power:']
 
def add_missing_labels(afe, hostname):
    """
    Queries the detectable labels supported by the given host,
    and adds those labels to the host.
 
    @param afe: A frontend.AFE() instance.
    @param hostname: The host to query and update.
 
    @return: True on success.
             False on failure to fetch labels or to add any individual label.
    """
    host = None
    try:
        host = hosts.create_host(hostname)
        labels = host.get_labels()
    except socket.gaierror:
        logging.warning('Unable to establish ssh connection to hostname '
                        '%s. Skipping.', hostname)
        return False
    except error.AutoservError:
        logging.warning('Unable to query labels on hostname %s. Skipping.',
                         hostname)
        return False
    finally:
        if host:
            host.close()
 
    afe_host = afe.get_hosts(hostname=hostname)[0]
 
    label_matches = afe.get_labels(name__in=labels)
 
    for label in label_matches:
        singleton_prefixes = [p for p in SINGLETON_LABEL_PREFIX
                              if label.name.startswith(p)]
        if len(singleton_prefixes) == 1:
            singleton_prefix = singleton_prefixes[0]
            # Delete existing label with `singleton_prefix`
            labels_to_delete = [l for l in afe_host.labels
                                if l.startswith(singleton_prefix) and
                                not l in labels]
            if labels_to_delete:
                logging.warning('Removing label %s', labels_to_delete)
                afe_labels_to_delete = afe.get_labels(name__in=labels_to_delete)
                for afe_label in afe_labels_to_delete:
                    afe_label.remove_hosts(hosts=[hostname])
        label.add_hosts(hosts=[hostname])
 
    missing_labels = set(labels) - set([l.name for l in label_matches])
 
    if missing_labels:
        for label in missing_labels:
            logging.warning('Unable to add label %s to host %s. '
                            'Skipping unknown label.', label, hostname)
        return False
 
    return True
 
 
def main():
    """"
    Entry point for add_detected_host_labels script.
    """
 
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', '--silent', dest='silent', action='store_true',
                        help='Suppress all but critical logging messages.')
    parser.add_argument('-i', '--info', dest='info_only', action='store_true',
                        help='Suppress logging messages below INFO priority.')
    parser.add_argument('-m', '--machines', dest='machines',
                        help='Comma separated list of machines to check.')
    options = parser.parse_args()
 
    if options.silent and options.info_only:
        print 'The -i and -s flags cannot be used together.'
        parser.print_help()
        return 0
 
 
    if options.silent:
        logging.disable(logging.CRITICAL)
 
    if options.info_only:
        logging.disable(logging.DEBUG)
 
    threadpool = pool.ThreadPool()
    afe = frontend.AFE()
 
    if options.machines:
        hostnames = [m.strip() for m in options.machines.split(',')]
    else:
        hostnames = afe.get_hostnames()
    successes = sum(threadpool.imap_unordered(
                        lambda x: add_missing_labels(afe, x),
                        hostnames))
    attempts = len(hostnames)
 
    logging.info('Label updating finished. Failed update on %d out of %d '
                 'hosts.', attempts-successes, attempts)
 
    return 0
 
 
if __name__ == '__main__':
    sys.exit(main())