171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng# Use of this source code is governed by a BSD-style license that can be 371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng# found in the LICENSE file. 471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng"""This file provides util functions used by RPM infrastructure.""" 771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 99c582892f78a51624817411c69132f3ba6d68a98Fang Dengimport collections 1071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengimport csv 1171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengimport logging 129b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Dengimport os 139b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Dengimport time 1471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 159c582892f78a51624817411c69132f3ba6d68a98Fang Dengimport common 169c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengimport rpm_infrastructure_exception 1871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengfrom config import rpm_config 199c582892f78a51624817411c69132f3ba6d68a98Fang Dengfrom autotest_lib.client.common_lib import enum 2071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 2171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 225e4e46d80fa532887f4517c63d29590071468535Fang DengMAPPING_FILE = os.path.join( 235e4e46d80fa532887f4517c63d29590071468535Fang Deng os.path.dirname(__file__), 245e4e46d80fa532887f4517c63d29590071468535Fang Deng rpm_config.get('CiscoPOE', 'servo_interface_mapping_file')) 2571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 2671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 279c582892f78a51624817411c69132f3ba6d68a98Fang DengPOWERUNIT_HOSTNAME_KEY = 'powerunit_hostname' 289c582892f78a51624817411c69132f3ba6d68a98Fang DengPOWERUNIT_OUTLET_KEY = 'powerunit_outlet' 299c582892f78a51624817411c69132f3ba6d68a98Fang DengHYDRA_HOSTNAME_KEY = 'hydra_hostname' 309b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang DengDEFAULT_EXPIRATION_SECS = 60 * 30 319c582892f78a51624817411c69132f3ba6d68a98Fang Deng 329c582892f78a51624817411c69132f3ba6d68a98Fang Dengclass PowerUnitInfo(object): 339c582892f78a51624817411c69132f3ba6d68a98Fang Deng """A class that wraps rpm/poe information of a device.""" 349c582892f78a51624817411c69132f3ba6d68a98Fang Deng 359c582892f78a51624817411c69132f3ba6d68a98Fang Deng POWERUNIT_TYPES = enum.Enum('POE', 'RPM', string_value=True) 369c582892f78a51624817411c69132f3ba6d68a98Fang Deng 379c582892f78a51624817411c69132f3ba6d68a98Fang Deng def __init__(self, device_hostname, powerunit_type, 389c582892f78a51624817411c69132f3ba6d68a98Fang Deng powerunit_hostname, outlet, hydra_hostname=None): 399c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.device_hostname = device_hostname 409c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.powerunit_type = powerunit_type 419c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.powerunit_hostname = powerunit_hostname 429c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.outlet = outlet 439c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.hydra_hostname = hydra_hostname 449c582892f78a51624817411c69132f3ba6d68a98Fang Deng 459c582892f78a51624817411c69132f3ba6d68a98Fang Deng 469c582892f78a51624817411c69132f3ba6d68a98Fang Deng @staticmethod 479c582892f78a51624817411c69132f3ba6d68a98Fang Deng def get_powerunit_info(afe_host): 489c582892f78a51624817411c69132f3ba6d68a98Fang Deng """Constructe a PowerUnitInfo instance from an afe host. 499c582892f78a51624817411c69132f3ba6d68a98Fang Deng 509c582892f78a51624817411c69132f3ba6d68a98Fang Deng @param afe_host: A host object. 519c582892f78a51624817411c69132f3ba6d68a98Fang Deng 529c582892f78a51624817411c69132f3ba6d68a98Fang Deng @returns: A PowerUnitInfo object populated with the power management 539c582892f78a51624817411c69132f3ba6d68a98Fang Deng unit information of the host. 549c582892f78a51624817411c69132f3ba6d68a98Fang Deng """ 559c582892f78a51624817411c69132f3ba6d68a98Fang Deng if (not POWERUNIT_HOSTNAME_KEY in afe_host.attributes or 569c582892f78a51624817411c69132f3ba6d68a98Fang Deng not POWERUNIT_OUTLET_KEY in afe_host.attributes): 579c582892f78a51624817411c69132f3ba6d68a98Fang Deng raise rpm_infrastructure_exception.RPMInfrastructureException( 589c582892f78a51624817411c69132f3ba6d68a98Fang Deng 'Can not retrieve complete rpm information' 599c582892f78a51624817411c69132f3ba6d68a98Fang Deng 'from AFE for %s, please make sure %s and %s are' 609c582892f78a51624817411c69132f3ba6d68a98Fang Deng ' in the host\'s attributes.' % (afe_host.hostname, 619c582892f78a51624817411c69132f3ba6d68a98Fang Deng POWERUNIT_HOSTNAME_KEY, POWERUNIT_OUTLET_KEY)) 629c582892f78a51624817411c69132f3ba6d68a98Fang Deng 639c582892f78a51624817411c69132f3ba6d68a98Fang Deng hydra_hostname=(afe_host.attributes[HYDRA_HOSTNAME_KEY] 649c582892f78a51624817411c69132f3ba6d68a98Fang Deng if HYDRA_HOSTNAME_KEY in afe_host.attributes 659c582892f78a51624817411c69132f3ba6d68a98Fang Deng else None) 669c582892f78a51624817411c69132f3ba6d68a98Fang Deng return PowerUnitInfo( 679c582892f78a51624817411c69132f3ba6d68a98Fang Deng device_hostname=afe_host.hostname, 689c582892f78a51624817411c69132f3ba6d68a98Fang Deng powerunit_type=PowerUnitInfo.POWERUNIT_TYPES.RPM, 699c582892f78a51624817411c69132f3ba6d68a98Fang Deng powerunit_hostname=afe_host.attributes[POWERUNIT_HOSTNAME_KEY], 709c582892f78a51624817411c69132f3ba6d68a98Fang Deng outlet=afe_host.attributes[POWERUNIT_OUTLET_KEY], 719c582892f78a51624817411c69132f3ba6d68a98Fang Deng hydra_hostname=hydra_hostname) 729c582892f78a51624817411c69132f3ba6d68a98Fang Deng 739c582892f78a51624817411c69132f3ba6d68a98Fang Deng 749c582892f78a51624817411c69132f3ba6d68a98Fang Dengclass LRUCache(object): 759c582892f78a51624817411c69132f3ba6d68a98Fang Deng """A simple implementation of LRU Cache.""" 769c582892f78a51624817411c69132f3ba6d68a98Fang Deng 779c582892f78a51624817411c69132f3ba6d68a98Fang Deng 789b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng def __init__(self, size, expiration_secs=DEFAULT_EXPIRATION_SECS): 799b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng """Initialize. 809b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng 819b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng @param size: Size of the cache. 829b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng @param expiration_secs: The items expire after |expiration_secs| 839b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng Set to None so that items never expire. 849b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng Default to DEFAULT_EXPIRATION_SECS. 859b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng """ 869c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.size = size 879c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.cache = collections.OrderedDict() 889b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.timestamps = {} 899b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.expiration_secs = expiration_secs 909c582892f78a51624817411c69132f3ba6d68a98Fang Deng 919c582892f78a51624817411c69132f3ba6d68a98Fang Deng 929c582892f78a51624817411c69132f3ba6d68a98Fang Deng def __getitem__(self, key): 939c582892f78a51624817411c69132f3ba6d68a98Fang Deng """Get an item from the cache""" 949c582892f78a51624817411c69132f3ba6d68a98Fang Deng # pop and insert the element again so that it 959c582892f78a51624817411c69132f3ba6d68a98Fang Deng # is moved to the end. 969c582892f78a51624817411c69132f3ba6d68a98Fang Deng value = self.cache.pop(key) 979c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.cache[key] = value 989c582892f78a51624817411c69132f3ba6d68a98Fang Deng return value 999c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1009c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1019c582892f78a51624817411c69132f3ba6d68a98Fang Deng def __setitem__(self, key, value): 1029c582892f78a51624817411c69132f3ba6d68a98Fang Deng """Insert an item into the cache.""" 1039c582892f78a51624817411c69132f3ba6d68a98Fang Deng if key in self.cache: 1049c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.cache.pop(key) 1059c582892f78a51624817411c69132f3ba6d68a98Fang Deng elif len(self.cache) == self.size: 1069b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng removed_key, _ = self.cache.popitem(last=False) 1079b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.timestamps.pop(removed_key) 1089c582892f78a51624817411c69132f3ba6d68a98Fang Deng self.cache[key] = value 1099b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.timestamps[key] = time.time() 1109c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1119c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1129c582892f78a51624817411c69132f3ba6d68a98Fang Deng def __contains__(self, key): 1139c582892f78a51624817411c69132f3ba6d68a98Fang Deng """Check whether a key is in the cache.""" 1149b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng if (self.expiration_secs is not None and 1159b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng key in self.timestamps and 1169b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng time.time() - self.timestamps[key] > self.expiration_secs): 1179b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.cache.pop(key) 1189b6c73bc6bb083ee20c2114e7595d5dde86b58a8Fang Deng self.timestamps.pop(key) 1199c582892f78a51624817411c69132f3ba6d68a98Fang Deng return key in self.cache 1209c582892f78a51624817411c69132f3ba6d68a98Fang Deng 1219c582892f78a51624817411c69132f3ba6d68a98Fang Deng 12271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengdef load_servo_interface_mapping(mapping_file=MAPPING_FILE): 12371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng """ 12471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng Load servo-switch-interface mapping from a CSV file. 12571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 12671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng In the file, the first column represents servo hostnames, 12771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng the second column represents switch hostnames, the third column 12871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng represents interface names. Columns are saparated by comma. 12971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 13071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng chromeos1-rack3-host12-servo,chromeos1-poe-switch1,fa31 13171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng chromeos1-rack4-host2-servo,chromeos1-poe-switch1,fa32 13271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng ,chromeos1-poe-switch1,fa33 13371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng ... 13471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 13571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng A row without a servo hostname indicates that no servo 13671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng has been connected to the corresponding interface. 13771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng This method ignores such rows. 13871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 13971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @param mapping_file: A csv file that stores the mapping. 14071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng If None, the setting in rpm_config.ini will be used. 14171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 14271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @return a dictionary that maps servo host name to a 14371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng tuple of switch hostname and interface. 14471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng e.g. { 14571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 'chromeos1-rack3-host12-servo': ('chromeos1-poe-switch1', 'fa31') 14671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng ...} 14771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 14871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @raises: rpm_infrastructure_exception.RPMInfrastructureException 14971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng when arg mapping_file is None. 15071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng """ 15171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng if not mapping_file: 15271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng raise rpm_infrastructure_exception.RPMInfrastructureException( 15371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 'mapping_file is None.') 15471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng servo_interface = {} 15571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng with open(mapping_file) as csvfile: 15671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng reader = csv.reader(csvfile, delimiter=',') 15771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng for row in reader: 15871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng servo_hostname = row[0].strip() 15971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng switch_hostname = row[1].strip() 16071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng interface = row[2].strip() 16171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng if servo_hostname: 16271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng servo_interface[servo_hostname] = (switch_hostname, interface) 16371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng return servo_interface 16471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 16571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 16671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Dengdef reload_servo_interface_mapping_if_necessary( 16771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng check_point, mapping_file=MAPPING_FILE): 16871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng """Reload the servo-interface mapping file if it is modified. 16971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 17071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng This method checks if the last-modified time of |mapping_file| is 17171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng later than |check_point|, if so, it reloads the file. 17271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 17371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @param check_point: A float number representing a time, used to determine 17471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng whether we need to reload the mapping file. 17571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @param mapping_file: A csv file that stores the mapping, if none, 17671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng the setting in rpm_config.ini will be used. 17771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 17871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @return: If the file is reloaded, returns a tuple 17971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng (last_modified_time, servo_interface) where 18071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng the first element is the last_modified_time of the 18171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng mapping file, the second element is a dictionary that 18271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng maps servo hostname to (switch hostname, interface). 18371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng If the file is not reloaded, return None. 18471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 18571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng @raises: rpm_infrastructure_exception.RPMInfrastructureException 18671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng when arg mapping_file is None. 18771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng """ 18871c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng if not mapping_file: 18971c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng raise rpm_infrastructure_exception.RPMInfrastructureException( 19071c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng 'mapping_file is None.') 19171c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng last_modified = os.path.getmtime(mapping_file) 19271c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng if check_point < last_modified: 19371c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng servo_interface = load_servo_interface_mapping(mapping_file) 19471c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng logging.info('Servo-interface mapping file %s is reloaded.', 19571c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng mapping_file) 19671c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng return (last_modified, servo_interface) 19771c4b1f8a48a8a6607e823a90c0e6db8a70e6b7aFang Deng return None 198