1d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng#! /usr/bin/python
2d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
3d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
4d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng# Use of this source code is governed by a BSD-style license that can be
5d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng# found in the LICENSE file.
6d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
7d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng"""
8d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengThis script generates a csv file containing the mapping of
9d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng(device_hostname, rpm_hostname, outlet, hydra_hostname) for each
10d946f3ffc0c02389e39b9486add0714d6a320e3eFang Denghost in our lab. The csv file is in the following format.
11d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
12d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengchromeos-rack2-host1,chromeos-rack2-rpm1,.A1,chromeos-197-hydra1.mtv
13d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengchromeos-rack2-host2,chromeos-rack2-rpm1,.A2,chromeos-197-hydra1.mtv
14d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng...
15d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
16d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengThe generated csv file can be used as input to add_host_powerunit_info.py
17d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
18d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengWorkflow:
19d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    <Generate the csv file>
20d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    python generate_rpm_mapping.py --csv mapping_file.csv --server cautotest
21d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
22d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    <Upload mapping information in csv file to AFE>
23d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    python add_host_powerunit_info.py --csv mapping_file.csv
24d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
25d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng"""
26d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport argparse
27d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport collections
28d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport logging
29d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport re
30d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport sys
31d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
32d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengimport common
33d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
34d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengfrom autotest_lib.client.common_lib import enum
35d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengfrom autotest_lib.server.cros.dynamic_suite import frontend_wrappers
36d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
37d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengCHROMEOS_LABS = enum.Enum('OysterBay', 'Atlantis', 'Chaos', 'Destiny', start_value=1)
38d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengHOST_REGX = 'chromeos(\d+)(-row(\d+))*-rack(\d+)-host(\d+)'
39d946f3ffc0c02389e39b9486add0714d6a320e3eFang DengDeviceHostname = collections.namedtuple(
40d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        'DeviceHostname', ['lab', 'row', 'rack', 'host'])
41d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
42d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
43d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengclass BaseLabConfig(object):
44d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Base class for a lab configuration."""
45d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    RPM_OUTLET_MAP = {}
46d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    LAB_NUMBER = -1
47d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
48d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
49d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_hostname(cls, device_hostname):
50d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm hostname given a device.
51d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
52047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
53d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
54d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: the rpm hostname, default to empty string.
55d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
56d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
57d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return ''
58d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
59d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
60d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
61d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_outlet(cls, device_hostname):
62d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm outlet given a device.
63d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
64047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
65d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
66d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: the rpm outlet, default to empty string.
67d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
68d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
69d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return ''
70d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
71d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
72d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
73d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_hydra_hostname(cls, device_hostname):
74d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get hydra hostname given a device.
75d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
76047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
77d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
78d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: the hydra hostname, default to empty string.
79d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
80d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
81d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return ''
82d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
83d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
84d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
85d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def is_device_in_the_lab(cls, device_hostname):
86d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Check whether a dut belongs to the lab.
87d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
88047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
89d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
90d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: True if the dut belongs to the lab,
91d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                  False otherwise.
92d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
93d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
94d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return device_hostname.lab == cls.LAB_NUMBER
95d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
96d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
97d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengclass OysterBayConfig(BaseLabConfig):
98d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Configuration for OysterBay"""
99d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
100d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    LAB_NUMBER = CHROMEOS_LABS.OYSTERBAY
101d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
102d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
103d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
104d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_hostname(cls, device_hostname):
105d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm hostname.
106d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
107047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
108d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
109d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hostname of the rpm that has the device.
110d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
111d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
112b705433165c2fbacc08f541bb3f314c9376df8f5Fang Deng        if not device_hostname.row:
113b705433165c2fbacc08f541bb3f314c9376df8f5Fang Deng            return ''
114d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return 'chromeos%d-row%d-rack%d-rpm1' % (
115d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.lab, device_hostname.row,
116d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.rack)
117d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
118d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
119d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
120d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_outlet(cls, device_hostname):
121d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm outlet.
122d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
123047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
124d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
125d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: rpm outlet, e.g. '.A1'
126d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
127d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
128b705433165c2fbacc08f541bb3f314c9376df8f5Fang Deng        if not device_hostname.row:
129b705433165c2fbacc08f541bb3f314c9376df8f5Fang Deng            return ''
130d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return '.A%d' % device_hostname.host
131d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
132d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
133d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengclass AtlantisConfig(BaseLabConfig):
134d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Configuration for Atlantis lab."""
135d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
136d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    LAB_NUMBER = CHROMEOS_LABS.ATLANTIS
137d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    # chromeos2, hostX -> outlet
138d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    RPM_OUTLET_MAP = {
139d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            1: 1,
140d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            7: 2,
141d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            2: 4,
142d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            8: 5,
143d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            3: 7,
144d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            9: 8,
145d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            4: 9,
146d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            10: 10,
147d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            5: 12,
148d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            11: 13,
149d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            6: 15,
150d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            12: 16}
151d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
152d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
153d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_hostname(cls, device_hostname):
154d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm hostname.
155d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
156047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
157d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
158d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hostname of the rpm that has the device.
159d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
160d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
161d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return 'chromeos%d-row%d-rack%d-rpm1' % (
162d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.lab, device_hostname.row,
163d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.rack)
164d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
165d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
166d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
167d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_outlet(cls, device_hostname):
168d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm outlet.
169d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
170047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
171d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
172d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: rpm outlet, e.g. '.A1'
173d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
174d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
175d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return '.A%d' % cls.RPM_OUTLET_MAP[device_hostname.host]
176d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
177d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
178d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
179d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_hydra_hostname(cls, device_hostname):
180d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get hydra hostname.
181d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
182047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
183d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
184d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hydra hostname
185d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
186d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
187d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        row = device_hostname.row
188d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        rack = device_hostname.rack
189d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        if row >= 1 and row <= 5 and rack >= 1 and rack <= 7:
190d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-197-hydra1.cros'
191d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        elif row >= 1 and row <= 5 and rack >= 8 and rack <= 11:
192d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-197-hydra2.cros'
193d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        else:
194d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            logging.error('Could not determine hydra for %s',
195d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                          device_hostname)
196d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return ''
197d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
198d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
199d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengclass ChaosConfig(BaseLabConfig):
200d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Configuration for Chaos lab."""
201d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
202d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    LAB_NUMBER = CHROMEOS_LABS.CHAOS
203d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
204d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
205d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
206d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_hostname(cls, device_hostname):
207d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm hostname.
208d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
209047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
210d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
211d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hostname of the rpm that has the device.
212d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
213d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
214d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return 'chromeos%d-row%d-rack%d-rpm1' % (
215d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.lab, device_hostname.row,
216d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.rack)
217d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
218d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
219d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
220d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_outlet(cls, device_hostname):
221d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm outlet.
222d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
223047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
224d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
225d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: rpm outlet, e.g. '.A1'
226d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
227d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
228d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return '.A%d' % device_hostname.host
229d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
230d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
231d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengclass DestinyConfig(BaseLabConfig):
232d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Configuration for Desitny lab."""
233d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
234d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    LAB_NUMBER = CHROMEOS_LABS.DESTINY
235047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    # None-densified rack: one host per shelf
236047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    # (rowX % 2, hostY) -> outlet
237d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    RPM_OUTLET_MAP = {
238d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 1): 1,
239d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (0, 1): 2,
240d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 2): 4,
241d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (0, 2): 5,
242d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 3): 7,
243d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (0, 3): 8,
244d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 4): 9,
245d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (0, 4): 10,
246d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 5): 12,
247d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (0, 5): 13,
248d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            (1, 6): 15,
249047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 6): 16,
250047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    }
251047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
252047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    # Densified rack: one shelf can have two chromeboxes or one notebook.
253047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    # (rowX % 2, hostY) -> outlet
254047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    DENSIFIED_RPM_OUTLET_MAP = {
255047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 2):  1,  (1, 1): 1,
256047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 1):  2,  (0, 2): 2,
257047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 4):  3,  (1, 3): 3,
258047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 3):  4,  (0, 4): 4,
259047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 6):  5,  (1, 5): 5,
260047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 5):  6,  (0, 6): 6,
261047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 8):  7,  (1, 7): 7,
262047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 7):  8,  (0, 8): 8,
263047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            # outlet 9, 10 are not used
264047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 10): 11, (1, 9): 11,
265047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 9):  12, (0, 10): 12,
266047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 12): 13, (1, 11): 13,
267047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 11): 14, (0, 12): 14,
268047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 14): 15, (1, 13): 15,
269047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 13): 16, (0, 14): 16,
270047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 16): 17, (1, 15): 17,
271047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 15): 18, (0, 16): 18,
272047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 18): 19, (1, 17): 19,
273047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 17): 20, (0, 18): 20,
274047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 20): 21, (1, 19): 21,
275047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 19): 22, (0, 20): 22,
276047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (1, 22): 23, (1, 21): 23,
277047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            (0, 21): 24, (0, 22): 24,
278047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    }
279047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
280047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
281047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    @classmethod
282047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    def is_densified(cls, device_hostname):
283047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        """Whether the host is on a densified rack.
284047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
285047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
286047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
287047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @returns: True if on a densified rack, False otherwise.
288047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        """
289047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        return device_hostname.rack in (0, 12, 13)
290d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
291d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
292d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
293d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_hostname(cls, device_hostname):
294d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm hostname.
295d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
296047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
297d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
298d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hostname of the rpm that has the device.
299d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
300d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
301d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        row = device_hostname.row
302047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        if row == 13:
303047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            logging.warn('Rule not implemented for row 13 in chromeos4')
304047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            return ''
305047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
306047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        # rpm row is like chromeos4-row1_2-rackX-rpmY
307047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        rpm_row = ('%d_%d' % (row - 1, row) if row % 2 == 0 else
308047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng                   '%d_%d' % (row, row + 1))
309047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng
310047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        if cls.is_densified(device_hostname):
311047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            # Densified rack has two rpms, decide which one the host belongs to
312047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            # Rule:
313047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            #     odd row number,  even host number -> rpm1
314047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            #     odd row number,  odd host number  -> rpm2
315047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            #     even row number, odd host number  -> rpm1
316047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            #     even row number, even host number -> rpm2
317047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            rpm_number = 1 if (row + device_hostname.host) % 2 == 1 else 2
318d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        else:
319047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            # Non-densified rack only has one rpm
320047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            rpm_number = 1
321047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        return 'chromeos%d-row%s-rack%d-rpm%d' % (
322d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                device_hostname.lab,
323047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng                rpm_row, device_hostname.rack, rpm_number)
324d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
325d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
326d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
327d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_rpm_outlet(cls, device_hostname):
328d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get rpm outlet.
329d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
330047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
331d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
332d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: rpm outlet, e.g. '.A1'
333d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
334d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
335d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        try:
336047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            outlet_map = (cls.DENSIFIED_RPM_OUTLET_MAP
337047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng                          if cls.is_densified(device_hostname) else
338047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng                          cls.RPM_OUTLET_MAP)
339047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng            outlet_number = outlet_map[(device_hostname.row % 2,
340047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng                                        device_hostname.host)]
341d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return '.A%d' % outlet_number
342d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        except KeyError:
343d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            logging.error('Could not determine outlet for device %s',
344d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                          device_hostname)
345d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return ''
346d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
347d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
348d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @classmethod
349d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    def get_hydra_hostname(cls, device_hostname):
350d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """Get hydra hostname.
351d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
352047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng        @param device_hostname: A DeviceHostname named tuple.
353d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
354d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        @returns: hydra hostname
355d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
356d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        """
357d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        row = device_hostname.row
358d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        rack = device_hostname.rack
359d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        if row >= 1 and row <= 6 and rack >=1 and rack <= 11:
360d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-destiny-hydra1.cros'
361d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        elif row >= 7 and row <= 12 and rack >=1 and rack <= 11:
362d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-destiny-hydra2.cros'
363d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        elif row >= 1 and row <= 10 and rack >=12 and rack <= 13:
364d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-destiny-hydra3.cros'
365d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        elif row in [3, 4, 5, 6, 9, 10] and rack == 0:
366d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-destiny-hydra3.cros'
367d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        elif row == 13 and rack >= 0 and rack <= 11:
368d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return 'chromeos-destiny-hydra3.cros'
369d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        else:
370d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            logging.error('Could not determine hydra hostname for %s',
371d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                          device_hostname)
372d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            return ''
373d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
374d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
375d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengdef parse_device_hostname(device_hostname):
376d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Parse device_hostname to DeviceHostname object.
377d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
378d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @param device_hostname: A string, e.g. 'chromeos2-row2-rack4-host3'
379d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
380047183cbaa217e6b93252b8ee77d7eafef5b78acFang Deng    @returns: A DeviceHostname named tuple or None if the
381d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng              the hostname doesn't follow the pattern
382d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng              defined in HOST_REGX.
383d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
384d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """
385d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    m = re.match(HOST_REGX, device_hostname.strip())
386d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    if m:
387d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return DeviceHostname(
388d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                lab=int(m.group(1)),
389d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                row=int(m.group(3)) if m.group(3) else None,
390d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                rack=int(m.group(4)),
391d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                host=int(m.group(5)))
392d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    else:
393d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        logging.error('Could not parse %s', device_hostname)
394d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        return None
395d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
396d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
397d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengdef generate_mapping(hosts, lab_configs):
398d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Generate device_hostname-rpm-outlet-hydra mapping.
399d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
400d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @param hosts: hosts objects get from AFE.
401d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @param lab_configs: A list of configuration classes,
402d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        each one for a lab.
403d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
404d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @returns: A dictionary that maps device_hostname to
405d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng              (rpm_hostname, outlet, hydra_hostname)
406d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
407d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """
408d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    # device hostname -> (rpm_hostname, outlet, hydra_hostname)
409d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    rpm_mapping = {}
410d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    for host in hosts:
411d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        device_hostname = parse_device_hostname(host.hostname)
412d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        if not device_hostname:
413d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            continue
414d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        for lab in lab_configs:
415d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            if lab.is_device_in_the_lab(device_hostname):
416d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                rpm_hostname = lab.get_rpm_hostname(device_hostname)
417d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                rpm_outlet = lab.get_rpm_outlet(device_hostname)
418d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                hydra_hostname = lab.get_hydra_hostname(device_hostname)
419d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                if not rpm_hostname or not rpm_outlet:
420d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                    logging.error(
421d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                            'Skipping device %s: could not determine '
422d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                            'rpm hostname or outlet.', host.hostname)
423d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                    break
424d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                rpm_mapping[host.hostname] = (
425d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        rpm_hostname, rpm_outlet, hydra_hostname)
426d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                break
427d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        else:
428d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            logging.info(
429d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                    '%s is not in a know lab '
430d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                    '(oyster bay, atlantis, chaos, destiny)',
431d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                    host.hostname)
432d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    return rpm_mapping
433d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
434d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
435d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengdef output_csv(rpm_mapping, csv_file):
436d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """Dump the rpm mapping dictionary to csv file.
437d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
438d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @param rpm_mapping: A dictionary that maps device_hostname to
439d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        (rpm_hostname, outlet, hydra_hostname)
440d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    @param csv_file: The name of the file to write to.
441d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
442d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    """
443d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    with open(csv_file, 'w') as f:
444d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng        for hostname, rpm_info in rpm_mapping.iteritems():
445d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            line = ','.join(rpm_info)
446d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            line = ','.join([hostname, line])
447d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            f.write(line + '\n')
448d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
449d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
450d946f3ffc0c02389e39b9486add0714d6a320e3eFang Dengif __name__ == '__main__':
451d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    logging.basicConfig(level=logging.DEBUG)
452d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    parser = argparse.ArgumentParser(
453d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            description='Generate device_hostname-rpm-outlet-hydra mapping '
454d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        'file needed by add_host_powerunit_info.py')
455d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    parser.add_argument('--csv', type=str, dest='csv_file', required=True,
456d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        help='The path to the csv file where we are going to '
457d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                             'write the mapping information to.')
458d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    parser.add_argument('--server', type=str, dest='server', default=None,
459d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                        help='AFE server that the script will be talking to. '
460d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                             'If not specified, will default to using the '
461d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                             'server in global_config.ini')
462d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    options = parser.parse_args()
463d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng
464d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    AFE = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10,
465d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng                                        server=options.server)
466d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    logging.info('Connected to %s', AFE.server)
467d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    rpm_mapping = generate_mapping(
468d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            AFE.get_hosts(),
469d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng            [OysterBayConfig, AtlantisConfig, ChaosConfig, DestinyConfig])
470d946f3ffc0c02389e39b9486add0714d6a320e3eFang Deng    output_csv(rpm_mapping, options.csv_file)
471