blob: 2d0c94f5055227822aeb9678e7a1591cbd8e865b [file] [log] [blame]
# 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.
"""HWID specific rule function implementations."""
import factory_common # pylint: disable=W0611
from cros.factory.common import MakeList, MakeSet
from cros.factory.gooftool.vpd_data import KNOWN_VPD_FIELD_DATA
from cros.factory.hwid.common import HWIDException
from cros.factory.hwid.encoder import (
BOMToBinaryString, BinaryStringToEncodedString)
from cros.factory.test import phase
from cros.factory.test import registration_codes
from cros.factory.test.registration_codes import RegistrationCode
from cros.factory.rule import RuleFunction, Value, GetContext, GetLogger
def GetClassAttributesOnBOM(hwid, comp_cls):
"""Creates a set of valid rule values to be evaluated with.
This method accepts a HWID context and a component class, and generates a dict
of attributes under the HWID context. First it checks there is a valid
component of the given class in the BOM by matching the probed values in the
BOM object to the values defined in the database. Then it extracts from the
database all feasible values that can be used in rule evaluation (e.g.
component name, component values, labels, ... etc.), and return the values as
a dict. A dict with name maps to None is used to represent missing components.
For example, the valid attributes for 'storage' class may look like:
valid_attributes = {
'name': 'sandisk_16g',
'value': 'Sandisk 33456',
'labels': {
'technology': 'SSD',
'size': '16G'
}
}
Args:
hwid: The HWID context to extract attributes from.
comp_cls: The component class to retrieve values for.
Returns:
A dict of attributes extracted from database that can be used to represent
or describe the given component class. None if comp_cls is invalid.
"""
def PackProbedValues(bom, comp_cls):
results = []
for c in bom.components[comp_cls]:
if c.probed_values is None:
# For non-probeable components, report its component name directly if
# there is one.
if c.component_name:
results.append(c.component_name)
continue
matched_component = hwid.database.components.MatchComponentsFromValues(
comp_cls, c.probed_values)
if matched_component:
results.extend(matched_component.keys())
return results
if comp_cls not in hwid.database.components.GetRequiredComponents():
GetLogger().Error('Invalid component class: %r' % comp_cls)
return None
# Construct a set of known values from hwid.database and hwid.bom.
results = []
bom_components = PackProbedValues(hwid.bom, comp_cls)
for comp in bom_components:
try:
comp_attrs = hwid.database.components.GetComponentAttributes(comp_cls,
comp)
results.append(comp)
if 'labels' in comp_attrs:
results.extend(MakeList(comp_attrs['labels'].values()))
except HWIDException:
continue
# If the set is empty, add a None element indicating that the component
# class is missing.
if not results:
results.append(None)
return results
def CreateSetFromAttributes(attr_dict):
"""Create a set from the values of the given attribute dict.
Args:
attr_dict: A dict of attributes.
Returns:
A set object with elements consisting of the values from the given attribute
dict.
"""
result = set()
for attr in attr_dict.itervalues():
result |= MakeSet(attr)
return result
def _ComponentCompare(comp_cls, values, op_for_values):
"""Component comparison helper function.
Args:
comp_cls: The class of component to test.
values: A list of values to match.
op_for_values: The operation used to generate final result. Must be any or
all.
"""
context = GetContext()
attrs = GetClassAttributesOnBOM(context.hwid, comp_cls)
if attrs is None:
return False
values = [
Value(v) if not isinstance(v, Value) else v for v in MakeList(values)]
return op_for_values(
[any([v.Matches(attr) for attr in attrs]) for v in values])
@RuleFunction(['hwid'])
def ComponentEq(comp_cls, values):
"""Test if the component equals to the values set.
True if every value in 'values' has a match in the attributes of 'comp_cls'
Args:
comp_cls: The class of component to test.
values: A list of values to match.
Returns:
True if the component equals to the given values, False otherwise.
"""
return _ComponentCompare(comp_cls, values, all)
@RuleFunction(['hwid'])
def ComponentIn(comp_cls, values):
"""Test if the component is in the values set.
True if one value in 'values' has a match in the attributes of 'comp_cls'
Args:
comp_cls: The class of component to test.
values: A list of values to match.
Returns:
True if the component is in the given values, False otherwise.
"""
return _ComponentCompare(comp_cls, values, any)
@RuleFunction(['hwid'])
def SetComponent(comp_cls, name):
"""A wrapper method to update {comp_cls: name} pair of BOM and re-generate
'binary_string' and 'encoded_string' of the HWID context.
Args:
comp_cls: The component class to set.
name: The component name to set to the given component class.
"""
context = GetContext()
context.hwid.bom = context.hwid.database.UpdateComponentsOfBOM(
context.hwid.bom, {comp_cls: name})
context.hwid.binary_string = BOMToBinaryString(context.hwid.database,
context.hwid.bom)
context.hwid.encoded_string = BinaryStringToEncodedString(
context.hwid.database, context.hwid.binary_string)
@RuleFunction(['hwid'])
def SetImageId(image_id):
"""A function to set the image id of the given HWID context.
Args:
image_id: The image id to set.
"""
context = GetContext()
if isinstance(image_id, str):
# Convert image_id string to its corresponding encoded value.
reversed_image_id_dict = dict((value, key) for key, value in
context.hwid.database.image_id.iteritems())
if image_id not in reversed_image_id_dict:
raise HWIDException('Invalid image id: %r' % image_id)
image_id = reversed_image_id_dict[image_id]
if image_id not in context.hwid.database.image_id:
raise HWIDException('Invalid image id: %r' % image_id)
context.hwid.bom.image_id = image_id
context.hwid.binary_string = BOMToBinaryString(context.hwid.database,
context.hwid.bom)
context.hwid.encoded_string = BinaryStringToEncodedString(
context.hwid.database, context.hwid.binary_string)
@RuleFunction(['hwid'])
def GetImageId():
"""A function to get the image id from the given HWID context.
Returns:
The image id of the HWID context.
"""
return GetContext().hwid.bom.image_id
@RuleFunction(['hwid'])
def GetOperationMode():
"""A function to get the set of operation modes of the HWID context.
Returns:
The set of operations modes currently enabled on the given HWID context.
"""
return GetContext().hwid.mode
@RuleFunction(['device_info'])
def GetDeviceInfo(key, default=None):
"""A wrapper method to get device info from shopfloor server.
If a dict of device info is provided in the context, return the value of 'key'
in the given dict.
Args:
key: The key of the device info to get.
default: default value, only valid when it is not None.
Returns:
The device info value got.
"""
if default is not None:
return GetContext().device_info.get(key, default)
else:
return GetContext().device_info[key]
@RuleFunction(['vpd'])
def GetVPDValue(section, key):
"""A wrapper method to get VPD values on DUT.
If a dict of vpd is provided in the context, return the value of 'key' in
'section' of the given dict.
Args:
section: The section of VPD to read value from. ('ro' or 'rw')
key: The key of the VPD value to get.
Returns:
The VPD value got.
"""
return GetContext().vpd[section][key]
@RuleFunction(['vpd'])
def ValidVPDValue(section, key):
"""A wrapper method to verify VPD value.
Args:
section: The section of VPD to read value from. ('ro' or 'rw')
key: The key of the VPD value to get.
Raises:
HWIDException if the VPD value is invalid.
"""
value = GetVPDValue(section, key)
valid_values = KNOWN_VPD_FIELD_DATA.get(key, None)
if (valid_values and value not in valid_values) or (not value):
GetLogger().Error('Invalid VPD value %r of %r' % (value, key))
return False
return True
# pylint: disable=W0622
@RuleFunction(['hwid'])
def CheckRegistrationCode(code, type=None):
"""A wrapper method to verify registration code.
Args:
code: The registration code to verify.
type: The type of code required. This may be a member of the
RegistrationCodes.Type enum; the strings 'unique' or
'user' are synonyms for UNIQUE_CODE, and 'group' is a synonym for
GROUP_CODE.
Raises:
RegistrationCodeException if the code is invalid.
"""
if type and type not in RegistrationCode.Type:
if type in ['user', 'unique']:
type = RegistrationCode.Type.UNIQUE_CODE
elif type == 'group':
type = RegistrationCode.Type.GROUP_CODE
else:
raise ValueError('Unknown reg code type %r' % type)
# Board name is exactly the same as in the HWID, except lowercase (e.g.,
# "spring", not "daisy_spring").
board = GetContext().hwid.database.board.lower()
registration_codes.CheckRegistrationCode(code, type=type, device=board)
@RuleFunction([])
def GetPhase():
"""A wrapper method to get build phase.
Returns:
One of the build phases: PROTO, EVT, DVT, PVT_DOGFOOD, as specified in the
phase module.
"""
return str(phase.GetPhase())