liyujie
2025-08-28 786ff4f4ca2374bdd9177f2e24b503d43e7a3b93
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#! /usr/bin/python
 
# Copyright (c) 2014 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.
 
"""
Manage power unit information for autotest hosts.
 
  We store rpm hostname, outlet, hydra information for a host in cautotest
  as host attributes. This tool allows you to add/modify/view/backup
  rpm attributes for hosts.
 
* Add/Modify power unit attributes:
  Step 1: create csv:
    Put attributes in a csv file, e.g. mapping.csv.
    Each line in mapping.csv consists of
        device_hostname, powerunit_hostname, powerunit_outlet, hydra_hostname,
    seperated by comma. For example
 
    chromeos-rack2-host1,chromeos-rack2-rpm1,.A1,chromeos-197-hydra1.mtv,
    chromeos-rack2-host2,chromeos-rack2-rpm1,.A2,chromeos-197-hydra1.mtv,
 
  Step 2: run
    ./manage_powerunit_info.py upload --csv mapping_file.csv
 
* View power unit attributes:
    ./manage_powerunit_info.py list
        -m "chromeos-rack2-host1,chromeos-rack2-host2"
 
* Backup existing attributes for all hosts to a csv file:
    ./manage_powerunit_info.py backup --csv backup.csv
"""
import argparse
import csv
import logging
import os
import sys
 
import common
 
from autotest_lib.client.common_lib import global_config
from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
from autotest_lib.site_utils.rpm_control_system import utils as rpm_utils
 
 
# The host attribute key name for get rpm hostname.
POWERUNIT_KEYS = [rpm_utils.POWERUNIT_HOSTNAME_KEY,
                  rpm_utils.POWERUNIT_OUTLET_KEY,
                  rpm_utils.HYDRA_HOSTNAME_KEY]
DEFAULT_SERVER = global_config.global_config.get_config_value(
        'SERVER', 'hostname', default=None)
 
 
def add_powerunit_info_to_host(afe, device, keyvals):
    """Add keyvals to the host's attributes in AFE.
 
    @param afe: AFE server to talk to.
    @param device: the device hostname, e.g. 'chromeos1-rack1-host1'
    @param keyvals: A dictionary where keys are the values in POWERUNIT_KEYS.
                    These are the power unit info about the devcie that we
                    are going to insert to AFE as host attributes.
    """
    if not afe.get_hosts(hostname=device):
        logging.debug('No host named %s', device)
        return
 
    logging.info('Adding host attribues to %s: %s', device, keyvals)
    for key, val in keyvals.iteritems():
        afe.set_host_attribute(key, val, hostname=device)
 
 
def add_from_csv(afe, csv_file):
    """Read power unit information from csv and add to host attributes.
 
    @param afe: AFE server to talk to.
    @param csv_file: A csv file, each line consists of device_hostname,
                     powerunit_hostname powerunit_outlet, hydra_hostname
                     separated by comma.
    """
    with open(csv_file) as f:
        reader = csv.reader(f, delimiter=',')
        for row in reader:
            device = row[0].strip()
            hydra = row[3].strip()
            if not hydra:
                hydra = None
            keyvals = dict(zip(
                    POWERUNIT_KEYS,
                    [row[1].strip(), row[2].strip(), hydra]))
            add_powerunit_info_to_host(afe, device, keyvals)
 
 
def dump_to_csv(afe, csv_file):
    """Dump power unit info of all hosts to a csv file.
 
    @param afe: AFE server to talk to.
    @param csv_file: A file to store the power unit information.
 
    """
    logging.info('Back up host attribues to %s', csv_file)
    with open(csv_file, 'w') as f:
        hosts = afe.get_hosts()
        for h in hosts:
            logging.info('Proccessing %s', h.hostname)
            f.write(h.hostname + ',')
            for key in POWERUNIT_KEYS:
                f.write(h.attributes.get(key, '') + ',')
            f.write('\n')
 
 
def list_powerunit_info(afe, devices):
    """List power unit info for a list of hosts.
 
    @param afe: AFE server to talk to.
    @param devices: a list of device hostnames.
    """
    hosts = afe.get_hosts(hostname__in = devices)
    if not hosts:
        logging.error('No host found.')
    for h in hosts:
        info = h.hostname + ','
        for key in POWERUNIT_KEYS:
            info += h.attributes.get(key, '') + ','
        print info
 
 
def parse_options():
    """Parse options"""
    parser = argparse.ArgumentParser(
            description=__doc__,
            formatter_class=argparse.RawDescriptionHelpFormatter)
    action_help = (
            'upload: read rpm attributes from csv file and set the attributes. '
            'list: list current attributes for a list of hosts. '
            'backup: dump existing rpm attributes to a csv file (for backup).')
    parser.add_argument(
            'action', choices=('upload', 'list', 'backup'), help=action_help)
    parser.add_argument('-f', '--csv_file', type=str, dest='csv_file',
                        help='A path to a csv file. When upload, each line '
                             'should consist of device_name, powerunit_hostname, '
                             'powerunit_outlet, hydra_hostname, separated '
                             'by comma. When dump, the file will be generated.')
    parser.add_argument('-m', type=str, dest='hostnames', default='',
                        help='A list of machine hostnames seperated by comma, '
                             'applicable to "list" command')
    parser.add_argument('-s', '--server', type=str, dest='server',
                        default=DEFAULT_SERVER,
                        help='AFE server that the script will be talking to. '
                             'If not speicified, will default to using the '
                             'server in global_config.ini')
    options = parser.parse_args()
    if options.action == 'upload' or options.action =='backup':
        if not options.csv_file:
            logging.error('Please specifiy a file with -f/--csv')
            sys.exit(1)
        file_exists = os.path.exists(options.csv_file)
        if options.action == 'upload' and not file_exists:
            logging.error('%s is not a valid file.', options.csv_file)
            sys.exit(1)
        if options.action == 'backup' and file_exists:
            logging.error('%s already exists.', options.csv_file)
            sys.exit(1)
    if options.action == 'list' and not options.hostnames:
       logging.error('Please specify hostnames with -m')
       sys.exit(1)
    return options
 
 
if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    options = parse_options()
    afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10,
                                        server=options.server)
    logging.info('Connected to %s', afe.server)
    if options.action =='backup':
        dump_to_csv(afe, options.csv_file)
    elif options.action == 'upload':
        confirm_msg = ('Upload rpm mapping from %s, are you sure?'
                       % options.csv_file)
        confirm = raw_input("%s (y/N) " % confirm_msg).lower() == 'y'
        if confirm:
            add_from_csv(afe, options.csv_file)
    elif options.action == 'list':
        list_powerunit_info(afe, [h.strip() for h in options.hostnames.split(',')])