• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env vpython3
2# Copyright 2021 The ChromiumOS Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Marshal various scheduling configs. Store them into the UFS datastore
6through the Google datastore API.
7
8By default, this script converts a few config-related protos into datastore
9entities:
10
111. ConfigBundleList from 'hw_design/generated/configs.jsonproto'
122. DutAttributeList from
13  '.../chromiumos/src/config/generated/dut_attributes.jsonproto'
143. FlatConfigList from 'hw_design/generated/flattened.jsonproto'
154. DeviceStabilityList from
16  '.../chromiumos/infra/config/testingconfig/generated/device_stability.cfg'
17
18The lists are parsed and individual entities are extracted. Using the datastore
19client specified, it encodes the protos as datastore entities and stores them
20into the UFS datastore.
21"""
22
23import argparse
24import datetime
25import logging
26import os
27
28from google.cloud import datastore
29
30from checker import io_utils
31from common import proto_utils
32
33# type constants
34CB_INPUT_TYPE = 'chromiumos.config.payload.ConfigBundleList'
35CB_OUTPUT_TYPE = 'chromiumos.config.payload.ConfigBundle'
36DA_INPUT_TYPE = 'chromiumos.test.api.DutAttributeList'
37DA_OUTPUT_TYPE = 'chromiumos.test.api.DutAttribute'
38FC_INPUT_TYPE = 'chromiumos.config.payload.FlatConfigList'
39FC_OUTPUT_TYPE = 'chromiumos.config.payload.FlatConfig'
40DEV_STAB_INPUT_TYPE = 'chromiumos.test.dut.DeviceStabilityList'
41DEV_STAB_OUTPUT_TYPE = 'chromiumos.test.dut.DeviceStability'
42
43# UFS services
44UFS_DEV_PROJECT = 'unified-fleet-system-dev'
45UFS_PROD_PROJECT = 'unified-fleet-system'
46
47# datastore constants
48CONFIG_BUNDLE_KIND = 'ConfigBundle'
49DUT_ATTRIBUTE_KIND = 'DutAttribute'
50FLAT_CONFIG_KIND = 'FlatConfig'
51DEVICE_STABILITY_KIND = 'DeviceStability'
52
53
54def get_ufs_project(env):
55  """Return project name based on env argument."""
56  if env == 'dev':
57    return UFS_DEV_PROJECT
58  if env == 'prod':
59    return UFS_PROD_PROJECT
60  raise RuntimeError('get_ufs_project: environment %s not supported' % env)
61
62
63def generate_config_bundle_id(bundle):
64  """Generate ConfigBundleEntity id as ${program_id}-${design_id}.
65
66  It is possible the ConfigBundle has an empty design_list (e.g. because it is
67  from a program repo). In this case, return None.
68  """
69  if not bundle.design_list:
70    return None
71
72  return (bundle.design_list[0].program_id.value + '-' +
73          bundle.design_list[0].id.value).lower()
74
75
76def handle_config_bundle_list(cb_list_path, client):
77  """Take a path to a ConfigBundleList, iterate through the list and store into
78  UFS datastore based on env.
79  """
80  cb_list = io_utils.read_json_proto(
81      protodb.GetSymbol(CB_INPUT_TYPE)(), cb_list_path)
82
83  for config_bundle in cb_list.values:
84    update_config(config_bundle, client, flat=False)
85
86
87def generate_flat_config_id(bundle):
88  """Generate FlatConfigEntity id as ${program_id}-${design_id}-${design_config_id}
89  if design_config_id is available. Else ${program_id}-${design_id}."""
90  if bundle.hw_design_config.id.value:
91    return (bundle.hw_design.program_id.value + '-' \
92      + bundle.hw_design.id.value \
93      + '-' + bundle.hw_design_config.id.value).lower()
94  return (bundle.hw_design.program_id.value + '-' \
95    + bundle.hw_design.id.value).lower()
96
97
98def handle_flat_config_list(fc_list_path, client):
99  """Take a path to a FlatConfigList, iterate through the list and store into
100  UFS datastore based on env.
101  """
102  fc_list = io_utils.read_json_proto(
103      protodb.GetSymbol(FC_INPUT_TYPE)(), fc_list_path)
104
105  for flat_config in fc_list.values:
106    update_config(flat_config, client, flat=True)
107
108
109def update_config(config, client, flat=False):
110  """Take a ConfigBundle or FlatConfig and store it an an entity in the UFS datastore."""
111  if flat:
112    kind = FLAT_CONFIG_KIND
113    eid = generate_flat_config_id(config)
114  else:
115    kind = CONFIG_BUNDLE_KIND
116    eid = generate_config_bundle_id(config)
117
118    if not eid:
119      logging.info('no eid for config %s, skipping', config)
120      return
121
122  logging.info('update_config: handling %s', eid)
123
124  key = client.key(kind, eid)
125  entity = datastore.Entity(
126      key=key,
127      exclude_from_indexes=['ConfigData'],
128  )
129  entity['ConfigData'] = config.SerializeToString()
130  entity['Updated'] = datetime.datetime.now()
131
132  logging.info('update_config: putting entity into datastore for %s', eid)
133  client.put(entity)
134
135
136def handle_dut_attribute_list(dut_attr_list_path, client):
137  """Take a path to a DutAttributeList, iterate through the list and store into
138  UFS datastore based on env.
139  """
140  dut_attr_list = io_utils.read_json_proto(
141      protodb.GetSymbol(DA_INPUT_TYPE)(), dut_attr_list_path)
142
143  for dut_attribute in dut_attr_list.dut_attributes:
144    update_dut_attribute(dut_attribute, client)
145
146
147def update_dut_attribute(attr, client):
148  """Take a DutAttribute and store it in the UFS datastore as a DutAttributeEntity."""
149  eid = attr.id.value
150  logging.info('update_dut_attribute: handling %s', eid)
151
152  key = client.key(DUT_ATTRIBUTE_KIND, eid)
153  entity = datastore.Entity(
154      key=key,
155      exclude_from_indexes=['AttributeData'],
156  )
157  entity['AttributeData'] = attr.SerializeToString()
158  entity['Updated'] = datetime.datetime.now()
159
160  logging.info('update_dut_attribute: putting entity into datastore for %s',
161               eid)
162  client.put(entity)
163
164
165def handle_device_stability_list(dev_stab_list_path, client):
166  """Take a path to a DeviceStabilityList, iterate through the list and store
167  into UFS datastore based on env.
168  """
169  dev_stab_list = io_utils.read_json_proto(
170      protodb.GetSymbol(DEV_STAB_INPUT_TYPE)(), dev_stab_list_path)
171
172  all_boards = {}
173  for dev_stab in dev_stab_list.values:
174    update_device_stability(dev_stab, client)
175    for eid in dev_stab.dut_criteria[0].values:
176      all_boards[eid] = True
177
178  clean_up_device_stability(all_boards, client)
179
180
181def clean_up_device_stability(all_boards, client):
182  """Clean up boards/models that are deleted from device stability config files"""
183  query = client.query(kind=DEVICE_STABILITY_KIND)
184  query.keys_only()
185  to_delete_keys = []
186  for record in query.fetch():
187    if record.key.name not in all_boards:
188      to_delete_keys.append(record.key)
189
190  client.delete_multi(to_delete_keys)
191
192
193def update_device_stability(dev_stab, client):
194  """Take a DeviceStability and store it in the UFS datastore as a
195  DeviceStabilityEntity.
196  """
197  # TODO (justinsuen): May need to change this eventually. This assumes the use
198  # of a single model (DutAttribute ID design_id) when defining a
199  # DeviceStability entry.
200  # http://cs/chromeos_internal/infra/config/testingconfig/target_test_requirements_config_helper.star?l=155
201  for eid in dev_stab.dut_criteria[0].values:
202    logging.info('update_device_stability: handling %s', eid)
203
204    key = client.key(DEVICE_STABILITY_KIND, eid)
205    entity = datastore.Entity(
206        key=key,
207        exclude_from_indexes=['StabilityData'],
208    )
209    entity['StabilityData'] = dev_stab.SerializeToString()
210    entity['Updated'] = datetime.datetime.now()
211
212    logging.info(
213        'update_device_stability: putting entity into datastore for %s', eid)
214    client.put(entity)
215
216
217if __name__ == '__main__':
218  logging.basicConfig(level=logging.INFO)
219  parser = argparse.ArgumentParser(
220      description=__doc__,
221      formatter_class=argparse.RawDescriptionHelpFormatter,
222  )
223
224  parser.add_argument(
225      '--env',
226      type=str,
227      default='dev',
228      help='environment flag for UFS service',
229  )
230
231  # load database of protobuffer name -> Type
232  protodb = proto_utils.create_symbol_db()
233  options = parser.parse_args()
234  ufs_ds_client = datastore.Client(
235      project=get_ufs_project(options.env),
236      namespace="os",
237  )
238  script_dir = os.path.dirname(os.path.realpath(__file__))
239
240  handle_config_bundle_list("hw_design/generated/configs.jsonproto",
241                            ufs_ds_client)
242  handle_dut_attribute_list(
243      os.path.realpath(
244          os.path.join(
245              script_dir,
246              "../generated/dut_attributes.jsonproto",
247          )), ufs_ds_client)
248  handle_flat_config_list("hw_design/generated/flattened.jsonproto",
249                          ufs_ds_client)
250  handle_device_stability_list(
251      os.path.realpath(
252          os.path.join(
253              script_dir,
254              "../../config-internal/board_config/generated/device_stability.cfg"
255          )), ufs_ds_client)
256