blob: f4ef64ea64289d39bf14503c2565af7df74b6b53 [file] [log] [blame]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""API messages."""
import collections
import logging
from protorpc import message_types
from protorpc import messages
import pytz
from tradefed_cluster import api_messages
from tradefed_cluster.util import ndb_shim as ndb
from google.oauth2 import credentials
from google.oauth2 import service_account
from multitest_transport.plugins import base as plugins
from multitest_transport.models import build
from multitest_transport.models import event_log
from multitest_transport.models import ndb_models
from multitest_transport.models import sql_models
from multitest_transport.util import env
from multitest_transport.util import file_util
from multitest_transport.util import xts_result
_OAUTH2_CREDENTIALS_USERNAME = 'User Account'
_convert_func_map = {}
def Converter(from_cls, to_cls):
"""A decorator to register a message converter.
Args:
from_cls: a class to convert from.
to_cls: a class to convert to.
Returns:
a decorator function.
"""
def Decorator(func):
_convert_func_map[(from_cls, to_cls)] = func
return func
return Decorator
def Convert(obj, to_cls, from_cls=None):
"""Converts an object to a given class.
Args:
obj: an object.
to_cls: a class to convert to.
from_cls: a class to convert from (optional).
Returns:
a converted object.
Raises:
ValueError: if there's no registered converter function.
"""
if obj is None:
return None
from_cls = from_cls or type(obj)
func = _convert_func_map.get((from_cls, to_cls))
if not func:
raise ValueError('no convert function for %s -> %s' % (from_cls, to_cls))
return func(obj)
def ConvertList(lst, to_cls, from_cls=None):
"""Converts an object or a list objects to a given class.
Args:
lst: a list of objects.
to_cls: a class to convert to.
from_cls: a class to convert from (optional).
Returns:
a a list of conveted objects.
"""
if lst is None:
return None
return [Convert(obj, to_cls, from_cls) for obj in lst]
def ConvertToKey(model_cls, id_):
"""Convert to a ndb.Key object.
Args:
model_cls: a model class.
id_: an object ID.
Returns:
a ndb.Key object.
"""
try:
id_ = int(id_)
except ValueError:
pass
return ndb.Key(model_cls, id_)
def ConvertToKeyOrNone(model_cls, id_):
"""Convert to a ndb.Key object or None.
This returns None if id_ is None.
Args:
model_cls: a model class.
id_: an object ID.
Returns:
a ndb.Key object.
"""
if id_ is None:
return None
return ConvertToKey(model_cls, id_)
def ConvertToDeviceSpecs(run_target):
"""Convert a run target to device specs.
Args:
run_target: a run target.
Returns:
a list of device specs.
"""
if not run_target:
return []
return ['device_serial:%s' % s for s in run_target.split(';')]
def _AddTimezone(datetime, timezone=pytz.UTC):
"""Adds timezone to an NDB datetime (which doesn't store timezone)."""
if datetime:
return datetime.replace(tzinfo=timezone)
class AuthorizationInfo(messages.Message):
"""OAuth2 authorization information.
Attributes:
url: an authorization url.
is_manual: a boolean value which tell us to start automatic or manual
copy/paste flow
"""
url = messages.StringField(1, required=True)
is_manual = messages.BooleanField(2, required=True)
class SimpleMessage(messages.Message):
"""Simple string message."""
value = messages.StringField(1, required=True)
class NameValuePair(messages.Message):
"""A generic name-value pair to store an option.
Attributes:
name: a name.
value: a value.
"""
name = messages.StringField(1, required=True)
value = messages.StringField(2)
class NameMultiValuePair(messages.Message):
"""A generic name-multi value pair to store an option.
Attributes:
name: a name.
values: a value.
"""
name = messages.StringField(1, required=True)
values = messages.StringField(2, repeated=True)
def ConvertNameValuePairs(pairs, to_cls):
"""Convert message and NDB model name-value(s) pairs."""
is_multi = to_cls in (NameMultiValuePair, ndb_models.NameMultiValuePair)
d = collections.OrderedDict()
for pair in pairs:
if pair.name in d:
logging.warning('Duplicate key %s in %s', pair.name, pairs)
if is_multi:
d[pair.name] = to_cls(name=pair.name, values=pair.values)
else:
d[pair.name] = to_cls(name=pair.name, value=pair.value)
return list(d.values())
def ConvertNameValuePairsToDict(pairs):
"""Convert message or NDB model name-value(s) pairs to dict."""
d = collections.OrderedDict()
for pair in pairs:
d[pair.name] = pair.value
return d
class FileSegment(messages.Message):
"""Partial file content."""
offset = messages.IntegerField(1)
length = messages.IntegerField(2)
lines = messages.StringField(3, repeated=True)
@Converter(file_util.FileSegment, FileSegment)
def _FileSegmentConverter(obj):
lines = [line.decode() for line in obj.lines]
return FileSegment(offset=obj.offset, length=obj.length, lines=lines)
class EventLogEntry(messages.Message):
"""An event log entry."""
create_time = message_types.DateTimeField(1, required=True)
level = messages.EnumField(ndb_models.EventLogLevel, 2, required=True)
message = messages.StringField(3, required=True)
@Converter(ndb_models.EventLogEntry, EventLogEntry)
def _EventLogEntryConverter(obj):
return EventLogEntry(
create_time=_AddTimezone(obj.create_time),
level=obj.level,
message=obj.message)
class TestResourceParameters(messages.Message):
"""Repeated properties of TestResourceObj and TestResourceDef."""
decompress_files = messages.StringField(1, repeated=True)
@Converter(ndb_models.TestResourceParameters, TestResourceParameters)
def _TestResourceParametersConverter(obj):
return TestResourceParameters(decompress_files=obj.decompress_files)
@Converter(TestResourceParameters, ndb_models.TestResourceParameters)
def _TestResourceParametersMessageConverter(msg):
return ndb_models.TestResourceParameters(
decompress_files=msg.decompress_files)
class TestResourceDef(messages.Message):
"""A test resource definition for a test."""
name = messages.StringField(1, required=True)
default_download_url = messages.StringField(2)
test_resource_type = messages.EnumField(ndb_models.TestResourceType, 3)
decompress = messages.BooleanField(4)
decompress_dir = messages.StringField(5)
mount_zip = messages.BooleanField(6)
params = messages.MessageField(TestResourceParameters, 7)
@Converter(ndb_models.TestResourceDef, TestResourceDef)
def _TestResourceDefConverter(obj):
return TestResourceDef(
name=obj.name,
default_download_url=obj.default_download_url,
test_resource_type=obj.test_resource_type,
decompress=obj.decompress,
decompress_dir=obj.decompress_dir,
mount_zip=obj.mount_zip,
params=Convert(obj.params, TestResourceParameters))
@Converter(TestResourceDef, ndb_models.TestResourceDef)
def _TestResourceDefMessageConverter(msg):
return ndb_models.TestResourceDef(
name=msg.name,
default_download_url=msg.default_download_url,
test_resource_type=msg.test_resource_type,
decompress=msg.decompress,
decompress_dir=msg.decompress_dir,
mount_zip=msg.mount_zip,
params=Convert(msg.params, ndb_models.TestResourceParameters))
class TestRunParameter(messages.Message):
"""Test run paramerer."""
max_retry_on_test_failures = messages.IntegerField(1)
invocation_timeout_seconds = messages.IntegerField(2)
output_idle_timeout_seconds = messages.IntegerField(3)
@Converter(ndb_models.TestRunParameter, TestRunParameter)
def _TestRunParameterConverter(obj):
return TestRunParameter(
max_retry_on_test_failures=obj.max_retry_on_test_failures,
output_idle_timeout_seconds=obj.output_idle_timeout_seconds)
@Converter(TestRunParameter, ndb_models.TestRunParameter)
def _TestRunParameterMessageConverter(msg):
return ndb_models.TestRunParameter(
max_retry_on_test_failures=msg.max_retry_on_test_failures,
output_idle_timeout_seconds=msg.output_idle_timeout_seconds)
class Test(messages.Message):
"""A test."""
id = messages.StringField(1)
name = messages.StringField(2, required=True)
description = messages.StringField(3)
test_resource_defs = messages.MessageField(TestResourceDef, 4, repeated=True)
command = messages.StringField(5)
env_vars = messages.MessageField(NameValuePair, 6, repeated=True)
output_file_patterns = messages.StringField(7, repeated=True)
result_file = messages.StringField(8)
setup_scripts = messages.StringField(9, repeated=True)
jvm_options = messages.StringField(10, repeated=True)
java_properties = messages.MessageField(NameValuePair, 11, repeated=True)
context_file_dir = messages.StringField(12)
context_file_pattern = messages.StringField(13)
retry_command_line = messages.StringField(14)
runner_sharding_args = messages.StringField(15)
default_test_run_parameters = messages.MessageField(TestRunParameter, 16)
module_config_pattern = messages.StringField(17)
module_execution_args = messages.StringField(18)
visibility_type = messages.EnumField(
ndb_models.VisibilityType, 19, default=ndb_models.VisibilityType.PUBLIC
)
upload_file_patterns = messages.StringField(20, repeated=True)
use_dynamic_download_mcts = messages.BooleanField(21)
@Converter(ndb_models.Test, Test)
def _TestConverter(obj):
return Test(
id=str(obj.key.id()) if obj.key else None,
name=obj.name,
description=obj.description,
test_resource_defs=ConvertList(obj.test_resource_defs, TestResourceDef),
command=obj.command,
env_vars=ConvertNameValuePairs(obj.env_vars, NameValuePair),
output_file_patterns=obj.output_file_patterns,
result_file=obj.result_file,
setup_scripts=obj.setup_scripts,
jvm_options=obj.jvm_options,
java_properties=ConvertNameValuePairs(obj.java_properties, NameValuePair),
context_file_dir=obj.context_file_dir,
context_file_pattern=obj.context_file_pattern,
retry_command_line=obj.retry_command_line,
runner_sharding_args=obj.runner_sharding_args,
default_test_run_parameters=Convert(
obj.default_test_run_parameters, TestRunParameter
),
module_config_pattern=obj.module_config_pattern,
module_execution_args=obj.module_execution_args,
visibility_type=obj.visibility_type,
upload_file_patterns=obj.upload_file_patterns,
use_dynamic_download_mcts=obj.use_dynamic_download_mcts,
)
@Converter(Test, ndb_models.Test)
def _TestMessageConverter(msg):
return ndb_models.Test(
key=ConvertToKeyOrNone(ndb_models.Test, msg.id),
name=msg.name,
description=msg.description,
test_resource_defs=ConvertList(
msg.test_resource_defs, ndb_models.TestResourceDef
),
command=msg.command,
env_vars=ConvertNameValuePairs(msg.env_vars, ndb_models.NameValuePair),
setup_scripts=msg.setup_scripts,
output_file_patterns=msg.output_file_patterns,
result_file=msg.result_file,
jvm_options=msg.jvm_options,
java_properties=ConvertNameValuePairs(
msg.java_properties, ndb_models.NameValuePair
),
context_file_dir=msg.context_file_dir,
context_file_pattern=msg.context_file_pattern,
retry_command_line=msg.retry_command_line,
runner_sharding_args=msg.runner_sharding_args,
default_test_run_parameters=Convert(
msg.default_test_run_parameters, ndb_models.TestRunParameter
),
module_config_pattern=msg.module_config_pattern,
module_execution_args=msg.module_execution_args,
visibility_type=msg.visibility_type,
upload_file_patterns=msg.upload_file_patterns,
use_dynamic_download_mcts=msg.use_dynamic_download_mcts,
)
class TestList(messages.Message):
"""A list of tests."""
tests = messages.MessageField(Test, 1, repeated=True)
class CredentialsInfo(messages.Message):
"""Credentials info object."""
email = messages.StringField(1)
@Converter(credentials.Credentials, CredentialsInfo)
def _OauthCredentialsConverter(_):
# TODO: Get email from userinfo endpoint
return CredentialsInfo(
email=_OAUTH2_CREDENTIALS_USERNAME)
@Converter(service_account.Credentials, CredentialsInfo)
def _ServiceAccountCredentialsConverter(obj):
return CredentialsInfo(
email=obj.service_account_email)
class BuildChannelConfig(messages.Message):
"""A build channel configuration."""
id = messages.StringField(1)
name = messages.StringField(2, required=True)
provider_name = messages.StringField(3, required=True)
options = messages.MessageField(NameValuePair, 4, repeated=True)
@Converter(ndb_models.BuildChannelConfig, BuildChannelConfig)
def _BuildChannelConfigConverter(obj):
return BuildChannelConfig(
id=str(obj.key.id()) if obj.key else None,
name=obj.name,
provider_name=obj.provider_name,
options=ConvertNameValuePairs(obj.options, NameValuePair))
@Converter(BuildChannelConfig, ndb_models.BuildChannelConfig)
def _BuildChannelConfigMessageConverter(msg):
return ndb_models.BuildChannelConfig(
key=ConvertToKeyOrNone(ndb_models.BuildChannelConfig, msg.id),
name=msg.name,
provider_name=msg.provider_name,
options=ConvertNameValuePairs(msg.options, ndb_models.NameValuePair))
class BuildChannel(messages.Message):
"""A build channel, which combines channel config and provider properties."""
id = messages.StringField(1, required=True)
name = messages.StringField(2, required=True)
provider_name = messages.StringField(3, required=True)
auth_state = messages.EnumField(
ndb_models.AuthorizationState, 4, required=True)
auth_methods = messages.EnumField(
ndb_models.AuthorizationMethod, 5, repeated=True)
credentials = messages.MessageField(CredentialsInfo, 6)
build_item_path_type = messages.EnumField(
ndb_models.BuildItemPathType, 7, required=True)
@Converter(build.BuildChannel, BuildChannel)
def _BuildChannelConverter(obj):
return BuildChannel(
id=str(obj.id),
name=obj.name,
provider_name=obj.provider_name,
auth_state=obj.auth_state,
auth_methods=obj.auth_methods,
credentials=Convert(obj.credentials, CredentialsInfo),
build_item_path_type=obj.build_item_path_type)
class BuildChannelList(messages.Message):
"""A list of build channels."""
build_channels = messages.MessageField(BuildChannel, 1, repeated=True)
class OptionDef(messages.Message):
"""A plugin option definition."""
name = messages.StringField(1, required=True)
value_type = messages.StringField(2, required=True)
choices = messages.StringField(3, repeated=True)
default = messages.StringField(4)
@Converter(plugins.OptionDef, OptionDef)
def _OptionDefConverter(obj):
return OptionDef(
name=obj.name,
value_type=obj.value_type.__name__,
choices=obj.choices or [],
default=obj.default)
class BuildChannelProvider(messages.Message):
"""Information about a build channel provider."""
name = messages.StringField(1, required=True)
option_defs = messages.MessageField(OptionDef, 2, repeated=True)
class BuildChannelProviderList(messages.Message):
"""A list of build channel providers."""
build_channel_providers = messages.MessageField(
BuildChannelProvider, 1, repeated=True)
class BuildItem(messages.Message):
"""A build item."""
name = messages.StringField(1)
path = messages.StringField(2)
is_file = messages.BooleanField(3)
size = messages.IntegerField(4)
timestamp = message_types.DateTimeField(5)
description = messages.StringField(6)
@Converter(plugins.BuildItem, BuildItem)
def _BuildItemConverter(obj):
return BuildItem(
name=obj.name,
path=obj.path,
is_file=obj.is_file,
size=obj.size,
timestamp=obj.timestamp,
description=obj.description)
class BuildItemList(messages.Message):
"""A list of build items."""
build_items = messages.MessageField(BuildItem, 1, repeated=True)
next_page_token = messages.StringField(2)
class TradefedConfigObject(messages.Message):
class_name = messages.StringField(1, required=True)
option_values = messages.MessageField(NameMultiValuePair, 2, repeated=True)
@Converter(ndb_models.TradefedConfigObject, TradefedConfigObject)
def _TradefedConfigObjectConverter(obj):
return TradefedConfigObject(
class_name=obj.class_name,
option_values=ConvertNameValuePairs(
obj.option_values, NameMultiValuePair))
@Converter(TradefedConfigObject, ndb_models.TradefedConfigObject)
def _TradefedConfigObjectMessageConverter(msg):
return ndb_models.TradefedConfigObject(
class_name=msg.class_name,
option_values=ConvertNameValuePairs(
msg.option_values, ndb_models.NameMultiValuePair))
class ConfigSetInfo(messages.Message):
"""Metadata for a config set."""
url = messages.StringField(1, required=True)
hash = messages.StringField(2)
name = messages.StringField(3, required=True)
description = messages.StringField(4)
last_update_time = messages.StringField(5)
status = messages.EnumField(ndb_models.ConfigSetStatus, 6)
@Converter(ndb_models.ConfigSetInfo, ConfigSetInfo)
def _ConfigSetInfoConverter(obj):
return ConfigSetInfo(
url=obj.url,
hash=obj.hash,
name=obj.name,
description=obj.description,
last_update_time=obj.last_update_time,
)
@Converter(ConfigSetInfo, ndb_models.ConfigSetInfo)
def _ConfigSetInfoMessageConverter(msg):
return ndb_models.ConfigSetInfo(
key=ConvertToKeyOrNone(ndb_models.ConfigSetInfo, msg.url),
url=msg.url,
hash=msg.hash,
name=msg.name,
description=msg.description,
last_update_time=msg.last_update_time,
)
class ConfigSetInfoList(messages.Message):
config_set_infos = messages.MessageField(ConfigSetInfo, 1, repeated=True)
@Converter(ndb_models.ConfigSetInfoList, ConfigSetInfoList)
def _ConfigSetInfoListConverter(obj):
return ConfigSetInfoList(
config_set_infos=ConvertList(obj.config_set_infos, ConfigSetInfo))
@Converter(ConfigSetInfoList, ndb_models.ConfigSetInfoList)
def _ConfigSetInfoListMessageConverter(msg):
return ndb_models.ConfigSetInfoList(
config_set_infos=ConvertList(
msg.config_set_infos, ndb_models.ConfigSetInfo))
class DeviceAction(messages.Message):
"""A device action."""
id = messages.StringField(1)
name = messages.StringField(2, required=True)
description = messages.StringField(3)
test_resource_defs = messages.MessageField(TestResourceDef, 4, repeated=True)
tradefed_target_preparers = messages.MessageField(
TradefedConfigObject, 5, repeated=True)
device_type = messages.StringField(6)
tradefed_options = messages.MessageField(NameMultiValuePair, 7, repeated=True)
device_spec = messages.StringField(8)
@Converter(ndb_models.DeviceAction, DeviceAction)
def _DeviceActionConverter(obj):
return DeviceAction(
id=str(obj.key.id()) if obj.key else None,
name=obj.name,
description=obj.description,
test_resource_defs=ConvertList(obj.test_resource_defs, TestResourceDef),
tradefed_target_preparers=ConvertList(
obj.tradefed_target_preparers, TradefedConfigObject),
device_type=obj.device_type,
tradefed_options=ConvertNameValuePairs(
obj.tradefed_options, NameMultiValuePair),
device_spec=obj.device_spec)
@Converter(DeviceAction, ndb_models.DeviceAction)
def _DeviceActionMessageConverter(msg):
return ndb_models.DeviceAction(
key=ConvertToKeyOrNone(ndb_models.DeviceAction, msg.id),
name=msg.name,
description=msg.description,
test_resource_defs=ConvertList(
msg.test_resource_defs, ndb_models.TestResourceDef),
tradefed_target_preparers=ConvertList(
msg.tradefed_target_preparers, ndb_models.TradefedConfigObject),
device_type=msg.device_type,
tradefed_options=ConvertNameValuePairs(
msg.tradefed_options, ndb_models.NameMultiValuePair),
device_spec=msg.device_spec)
class DeviceActionList(messages.Message):
"""A list of device actions."""
device_actions = messages.MessageField(DeviceAction, 1, repeated=True)
class TestModuleResult(messages.Message):
"""Test module result."""
id = messages.StringField(1) # Legacy test results will not have an ID
name = messages.StringField(2, required=True)
complete = messages.BooleanField(3, required=True)
duration_ms = messages.IntegerField(4, required=True)
passed_tests = messages.IntegerField(5, required=True)
failed_tests = messages.IntegerField(6, required=True)
total_tests = messages.IntegerField(7, required=True)
error_message = messages.StringField(8)
@Converter(sql_models.TestModuleResult, TestModuleResult)
def _TestModuleResultConverter(obj):
return TestModuleResult(
id=obj.id,
name=obj.name,
complete=obj.complete,
duration_ms=obj.duration_ms,
passed_tests=obj.passed_tests,
failed_tests=obj.failed_tests,
total_tests=obj.total_tests,
error_message=obj.error_message,
)
@Converter(api_messages.TestGroupStatus, TestModuleResult)
def _LegacyTestModuleResultConverter(obj):
return TestModuleResult(
name=obj.name,
complete=obj.is_complete,
duration_ms=obj.elapsed_time,
passed_tests=obj.passed_test_count,
failed_tests=obj.failed_test_count,
total_tests=obj.total_test_count,
error_message=obj.failure_message,
)
class TestModuleResultList(messages.Message):
"""List of test module results."""
results = messages.MessageField(TestModuleResult, 1, repeated=True)
extra_info = messages.StringField(2)
class TestCaseResult(messages.Message):
"""Test case result."""
id = messages.IntegerField(1, required=True)
module_id = messages.StringField(2, required=True)
name = messages.StringField(3, required=True)
status = messages.EnumField(xts_result.TestStatus, 4, required=True)
error_message = messages.StringField(5)
stack_trace = messages.StringField(6)
@Converter(sql_models.TestCaseResult, TestCaseResult)
def _TestCaseResultConverter(obj):
return TestCaseResult(
id=obj.id,
module_id=obj.module_id,
name=obj.name,
status=obj.status,
error_message=obj.error_message,
stack_trace=obj.stack_trace,
)
class TestCaseResultList(messages.Message):
"""List of test case results with pagination."""
results = messages.MessageField(TestCaseResult, 1, repeated=True)
next_page_token = messages.StringField(2)
class TestRunAction(messages.Message):
"""A test run action."""
id = messages.StringField(1)
name = messages.StringField(2, required=True)
description = messages.StringField(3)
hook_class_name = messages.StringField(4, required=True)
phases = messages.EnumField(ndb_models.TestRunPhase, 5, repeated=True)
options = messages.MessageField(NameValuePair, 6, repeated=True)
tradefed_result_reporters = messages.MessageField(
TradefedConfigObject, 7, repeated=True)
authorization_state = messages.EnumField(ndb_models.AuthorizationState, 8)
credentials = messages.MessageField(CredentialsInfo, 9)
@Converter(ndb_models.TestRunAction, TestRunAction)
def _TestRunActionConverter(obj):
return TestRunAction(
id=str(obj.key.id()) if obj.key else None,
name=obj.name,
description=obj.description,
hook_class_name=obj.hook_class_name,
phases=obj.phases,
options=ConvertNameValuePairs(obj.options, NameValuePair),
tradefed_result_reporters=ConvertList(
obj.tradefed_result_reporters, TradefedConfigObject),
credentials=Convert(obj.credentials, CredentialsInfo))
@Converter(TestRunAction, ndb_models.TestRunAction)
def _TestRunActionMessageConverter(msg):
return ndb_models.TestRunAction(
key=ConvertToKeyOrNone(ndb_models.TestRunAction, msg.id),
name=msg.name,
description=msg.description,
hook_class_name=msg.hook_class_name,
phases=msg.phases,
options=ConvertNameValuePairs(msg.options, ndb_models.NameValuePair),
tradefed_result_reporters=ConvertList(
msg.tradefed_result_reporters, ndb_models.TradefedConfigObject))
class TestRunActionList(messages.Message):
"""A list of test run actions."""
actions = messages.MessageField(TestRunAction, 1, repeated=True)
class TestRunActionRef(messages.Message):
"""A test run action reference."""
action_id = messages.StringField(1)
options = messages.MessageField(NameValuePair, 2, repeated=True)
@Converter(ndb_models.TestRunActionRef, TestRunActionRef)
def _TestRunActionRefConverter(obj):
return TestRunActionRef(
action_id=str(obj.action_key.id()) if obj.action_key else None,
options=ConvertNameValuePairs(obj.options, NameValuePair))
@Converter(TestRunActionRef, ndb_models.TestRunActionRef)
def _TestRunActionRefMessageConverter(msg):
return ndb_models.TestRunActionRef(
action_key=ConvertToKeyOrNone(ndb_models.TestRunAction, msg.action_id),
options=ConvertNameValuePairs(msg.options, ndb_models.NameValuePair))
class TestRunActionRefList(messages.Message):
"""A list of test run action references."""
refs = messages.MessageField(TestRunActionRef, 1, repeated=True)
class TestRunHook(messages.Message):
"""Information about a test run hook."""
name = messages.StringField(1, required=True)
option_defs = messages.MessageField(OptionDef, 2, repeated=True)
class TestRunHookList(messages.Message):
"""A list of test run hook."""
test_run_hooks = messages.MessageField(TestRunHook, 1, repeated=True)
class TestResourceObj(messages.Message):
"""A test resource object for a test."""
name = messages.StringField(1)
url = messages.StringField(2)
cache_url = messages.StringField(3)
test_resource_type = messages.EnumField(ndb_models.TestResourceType, 4)
decompress = messages.BooleanField(5)
decompress_dir = messages.StringField(6)
mount_zip = messages.BooleanField(7)
params = messages.MessageField(TestResourceParameters, 8)
@Converter(ndb_models.TestResourceObj, TestResourceObj)
def _TestResourceObjConverter(obj):
return TestResourceObj(
name=obj.name,
url=obj.url,
cache_url=obj.cache_url,
test_resource_type=obj.test_resource_type,
decompress=obj.decompress,
decompress_dir=obj.decompress_dir,
mount_zip=obj.mount_zip,
params=Convert(obj.params, TestResourceParameters))
@Converter(TestResourceObj, ndb_models.TestResourceObj)
def _TestResourceObjMessageConverter(msg):
return ndb_models.TestResourceObj(
name=msg.name,
url=msg.url,
cache_url=msg.cache_url,
test_resource_type=msg.test_resource_type,
decompress=msg.decompress,
decompress_dir=msg.decompress_dir,
mount_zip=msg.mount_zip,
params=Convert(msg.params, ndb_models.TestResourceParameters))
class TestRunConfig(messages.Message):
"""A test run config."""
test_id = messages.StringField(1, required=True)
cluster = messages.StringField(
2, required=True, default=ndb_models.DEFAULT_CLUSTER
)
command = messages.StringField(3, required=True, default='')
retry_command = messages.StringField(4, required=True, default='')
device_specs = messages.StringField(5, repeated=True)
run_target = messages.StringField(6) # Deprecated
run_count = messages.IntegerField(7, required=True, default=1)
shard_count = messages.IntegerField(8, required=True, default=1)
sharding_mode = messages.EnumField(
ndb_models.ShardingMode, 9, default=ndb_models.ShardingMode.RUNNER)
extra_args = messages.StringField(10) # TODO: Deprecated
retry_extra_args = messages.StringField(11) # TODO: Deprecated
max_retry_on_test_failures = messages.IntegerField(12)
queue_timeout_seconds = messages.IntegerField(
13, required=True, default=env.DEFAULT_QUEUE_TIMEOUT_SECONDS)
invocation_timeout_seconds = messages.IntegerField(
14, required=True, default=env.DEFAULT_INVOCATION_TIMEOUT_SECONDS)
output_idle_timeout_seconds = messages.IntegerField(
15, default=env.DEFAULT_OUTPUT_IDLE_TIMEOUT_SECONDS)
before_device_action_ids = messages.StringField(16, repeated=True)
test_run_action_refs = messages.MessageField(
TestRunActionRef, 17, repeated=True)
test_resource_objs = messages.MessageField(
TestResourceObj, 18, repeated=True)
use_parallel_setup = messages.BooleanField(19, default=True)
allow_partial_device_match = messages.BooleanField(20, default=False)
@Converter(ndb_models.TestRunConfig, TestRunConfig)
def _TestRunConfigConverter(obj):
"""Converts test run configs."""
if (not obj.command or not obj.retry_command) and obj.test_key:
test = obj.test_key.get()
if not obj.command and test:
obj.command = test.command
if obj.extra_args:
obj.command += ' %s' % obj.extra_args
if not obj.retry_command and test:
obj.retry_command = test.retry_command_line
if obj.retry_extra_args:
obj.retry_command += ' %s' % obj.retry_extra_args
return TestRunConfig(
test_id=str(obj.test_key.id()) if obj.test_key else None,
cluster=obj.cluster,
command=obj.command,
retry_command=obj.retry_command,
device_specs=obj.device_specs or ConvertToDeviceSpecs(obj.run_target),
run_target=obj.run_target,
run_count=obj.run_count,
shard_count=obj.shard_count,
sharding_mode=obj.sharding_mode,
max_retry_on_test_failures=obj.max_retry_on_test_failures,
queue_timeout_seconds=obj.queue_timeout_seconds,
invocation_timeout_seconds=obj.invocation_timeout_seconds,
output_idle_timeout_seconds=obj.output_idle_timeout_seconds,
before_device_action_ids=[
str(key.id()) for key in obj.before_device_action_keys
],
test_run_action_refs=ConvertList(
obj.test_run_action_refs, TestRunActionRef),
test_resource_objs=ConvertList(obj.test_resource_objs, TestResourceObj),
use_parallel_setup=obj.use_parallel_setup,
allow_partial_device_match=obj.allow_partial_device_match)
@Converter(TestRunConfig, ndb_models.TestRunConfig)
def _TestRunConfigMessageConverter(msg):
return ndb_models.TestRunConfig(
test_key=ConvertToKeyOrNone(ndb_models.Test, msg.test_id),
cluster=msg.cluster,
command=msg.command,
retry_command=msg.retry_command,
device_specs=msg.device_specs,
run_target=msg.run_target,
run_count=msg.run_count,
shard_count=msg.shard_count,
sharding_mode=msg.sharding_mode,
extra_args=msg.extra_args, # TODO: Deprecated
retry_extra_args=msg.retry_extra_args, # TODO: Deprecated
max_retry_on_test_failures=msg.max_retry_on_test_failures,
queue_timeout_seconds=msg.queue_timeout_seconds,
invocation_timeout_seconds=msg.invocation_timeout_seconds,
output_idle_timeout_seconds=msg.output_idle_timeout_seconds,
before_device_action_keys=[
ConvertToKeyOrNone(ndb_models.DeviceAction, device_action_id)
for device_action_id in msg.before_device_action_ids
],
test_run_action_refs=ConvertList(
msg.test_run_action_refs, ndb_models.TestRunActionRef),
test_resource_objs=ConvertList(
msg.test_resource_objs, ndb_models.TestResourceObj),
use_parallel_setup=msg.use_parallel_setup,
allow_partial_device_match=msg.allow_partial_device_match)
class TestRunConfigList(messages.Message):
"""A list of test run configs."""
test_run_configs = messages.MessageField(TestRunConfig, 1, repeated=True)
@Converter(ndb_models.TestRunConfigList, TestRunConfigList)
def _TestRunConfigListConverter(obj):
return TestRunConfigList(
test_run_configs=ConvertList(obj.test_run_configs, TestRunConfig),)
@Converter(TestRunConfigList, ndb_models.TestRunConfigList)
def _TestRunConfigListMessageConverter(msg):
return ndb_models.TestRunConfigList(
test_run_configs=ConvertList(msg.test_run_configs,
ndb_models.TestRunConfig))
# TODO: Deprecate TestResourcePipe
class TestResourcePipe(messages.Message):
"""A test resource pipe for a test plan."""
name = messages.StringField(1, required=True)
url = messages.StringField(2)
test_resource_type = messages.EnumField(ndb_models.TestResourceType, 3)
@Converter(ndb_models.TestResourcePipe, TestResourcePipe)
def _TestResourcePipeConverter(obj):
return TestResourcePipe(
name=obj.name,
url=obj.url,
test_resource_type=obj.test_resource_type)
@Converter(TestResourcePipe, ndb_models.TestResourcePipe)
def _TestResourcePipeMessageConverter(msg):
return ndb_models.TestResourcePipe(
name=msg.name,
url=msg.url,
test_resource_type=msg.test_resource_type)
class TestRunSequence(messages.Message):
"""A list of test run configs to run as retries."""
id = messages.StringField(1)
state = messages.EnumField(ndb_models.TestRunSequenceState, 2)
test_run_configs = messages.MessageField(TestRunConfig, 3, repeated=True)
finished_test_run_ids = messages.StringField(4, repeated=True)
@Converter(ndb_models.TestRunSequence, TestRunSequence)
def _TestRunSequenceConverter(obj):
return TestRunSequence(
id=str(obj.key.id()),
state=obj.state,
test_run_configs=ConvertList(obj.test_run_configs, TestRunConfig),
finished_test_run_ids=obj.finished_test_run_ids)
@Converter(TestRunSequence, ndb_models.TestRunSequence)
def _TestRunSequenceMessageConverter(msg):
return ndb_models.TestRunSequence(
state=msg.state,
test_run_configs=ConvertList(msg.test_run_configs,
ndb_models.TestRunConfig),
finished_test_run_ids=msg.finished_test_run_ids)
class TestPlan(messages.Message):
"""A test plan."""
id = messages.StringField(1)
name = messages.StringField(2, required=True)
labels = messages.StringField(3, repeated=True)
cron_exp = messages.StringField(4)
cron_exp_timezone = messages.StringField(5, default='UTC')
test_run_configs = messages.MessageField(TestRunConfig, 6, repeated=True)
test_run_sequences = messages.MessageField(
TestRunConfigList, 7, repeated=True)
last_run_time = message_types.DateTimeField(8)
last_run_ids = messages.StringField(9, repeated=True)
last_run_error = messages.StringField(10)
next_run_time = message_types.DateTimeField(11)
@Converter(ndb_models.TestPlan, TestPlan)
def _TestPlanConverter(obj):
status = None
if obj.key.id():
status = ndb_models.TestPlanStatus.query(ancestor=obj.key).get()
return TestPlan(
id=str(obj.key.id()),
name=obj.name,
labels=obj.labels,
cron_exp=obj.cron_exp,
cron_exp_timezone=obj.cron_exp_timezone,
test_run_configs=ConvertList(obj.test_run_configs, TestRunConfig),
test_run_sequences=ConvertList(obj.test_run_sequences, TestRunConfigList),
last_run_time=_AddTimezone(status.last_run_time) if status else None,
last_run_ids=([str(key.id()) for key in status.last_run_keys]
if status else []),
last_run_error=status.last_run_error if status else None,
next_run_time=_AddTimezone(status.next_run_time) if status else None,
)
@Converter(TestPlan, ndb_models.TestPlan)
def _TestPlanMessageConverter(msg):
return ndb_models.TestPlan(
key=ConvertToKeyOrNone(ndb_models.TestPlan, getattr(msg, 'id')),
name=msg.name,
labels=msg.labels,
cron_exp=msg.cron_exp,
cron_exp_timezone=msg.cron_exp_timezone,
test_run_configs=ConvertList(
msg.test_run_configs, ndb_models.TestRunConfig),
test_run_sequences=ConvertList(
msg.test_run_sequences, ndb_models.TestRunConfigList))
class TestPlanList(messages.Message):
"""A list of test plans."""
test_plans = messages.MessageField(TestPlan, 1, repeated=True)
class TestContextObj(messages.Message):
"""A test context object."""
command_line = messages.StringField(1)
env_vars = messages.MessageField(NameValuePair, 2, repeated=True)
test_resources = messages.MessageField(TestResourceObj, 3, repeated=True)
@Converter(ndb_models.TestContextObj, TestContextObj)
def _TestContextObjConverter(obj):
return TestContextObj(
command_line=obj.command_line,
env_vars=ConvertNameValuePairs(obj.env_vars, NameValuePair),
test_resources=ConvertList(obj.test_resources, TestResourceObj))
class TestPackageInfo(messages.Message):
"""A test package info."""
build_number = messages.StringField(1)
target_architecture = messages.StringField(2)
name = messages.StringField(3)
fullname = messages.StringField(4)
version = messages.StringField(5)
@Converter(ndb_models.TestPackageInfo, TestPackageInfo)
def _TestPackageInfoConverter(obj):
return TestPackageInfo(
build_number=obj.build_number,
target_architecture=obj.target_architecture,
name=obj.name,
fullname=obj.fullname,
version=obj.version)
class TestDeviceInfo(messages.Message):
"""A test device info."""
device_serial = messages.StringField(1)
hostname = messages.StringField(2)
run_target = messages.StringField(3)
build_id = messages.StringField(4)
product = messages.StringField(5)
sdk_version = messages.StringField(7)
@Converter(ndb_models.TestDeviceInfo, TestDeviceInfo)
def _TestDeviceInfoConverter(obj):
return TestDeviceInfo(
device_serial=obj.device_serial,
hostname=obj.hostname,
run_target=obj.run_target,
build_id=obj.build_id,
product=obj.product,
sdk_version=obj.sdk_version)
class TestRun(messages.Message):
"""A test run."""
id = messages.StringField(1)
prev_test_run_id = messages.StringField(2)
user = messages.StringField(3)
labels = messages.StringField(4, repeated=True)
test_plan = messages.MessageField(TestPlan, 5)
test = messages.MessageField(Test, 6)
test_run_config = messages.MessageField(TestRunConfig, 7)
test_resources = messages.MessageField(TestResourceObj, 8, repeated=True)
state = messages.EnumField(ndb_models.TestRunState, 9)
state_info = messages.StringField(10)
output_url = messages.StringField(11)
prev_test_context = messages.MessageField(TestContextObj, 12)
next_test_context = messages.MessageField(TestContextObj, 13)
request_id = messages.StringField(14)
sequence_id = messages.StringField(15)
total_test_count = messages.IntegerField(16)
failed_test_count = messages.IntegerField(17)
failed_test_run_count = messages.IntegerField(18)
create_time = message_types.DateTimeField(19)
update_time = message_types.DateTimeField(20)
before_device_actions = messages.MessageField(DeviceAction, 21, repeated=True)
test_run_actions = messages.MessageField(TestRunAction, 22, repeated=True)
test_devices = messages.MessageField(TestDeviceInfo, 23, repeated=True)
test_package_info = messages.MessageField(TestPackageInfo, 24)
log_entries = messages.MessageField(EventLogEntry, 25, repeated=True)
@Converter(ndb_models.TestRun, TestRun)
def _TestRunConverter(obj):
"""Converts a ndb_models.TestRun object to a message."""
# TestRun.test stores a snapshot copy of a test at test run creation time.
test_msg = Convert(obj.test, Test)
test_run_config_msg = Convert(obj.test_run_config, TestRunConfig)
# Grab a valid test id from test_run_config since a snapshot copy is missing
# a key.
if test_msg and test_run_config_msg:
test_msg.id = test_run_config_msg.test_id
return TestRun(
id=str(obj.key.id()),
prev_test_run_id=(str(obj.prev_test_run_key.id())
if obj.prev_test_run_key else None),
user=obj.user,
labels=obj.labels,
test_plan=(
Convert(obj.test_plan_key.get(), TestPlan)
if obj.test_plan_key else None),
test=test_msg,
test_run_config=test_run_config_msg,
test_resources=ConvertList(obj.test_resources, TestResourceObj),
state=obj.state,
state_info=_GetTestRunStateInfo(obj),
output_url=(file_util.GetAppStorageUrl([obj.output_path])
if obj.output_path else None),
prev_test_context=Convert(obj.prev_test_context, TestContextObj),
next_test_context=Convert(obj.next_test_context, TestContextObj),
request_id=obj.request_id,
sequence_id=obj.sequence_id,
total_test_count=obj.total_test_count,
failed_test_count=obj.failed_test_count,
failed_test_run_count=obj.failed_test_run_count,
create_time=_AddTimezone(obj.create_time),
update_time=_AddTimezone(obj.update_time),
before_device_actions=ConvertList(
obj.before_device_actions, DeviceAction),
test_run_actions=ConvertList(obj.test_run_actions, TestRunAction),
test_devices=ConvertList(obj.test_devices, TestDeviceInfo),
test_package_info=Convert(obj.test_package_info, TestPackageInfo),
log_entries=ConvertList(event_log.GetEntries(obj), EventLogEntry),
)
def _GetTestRunStateInfo(test_run: ndb_models.TestRun):
"""Determine an ndb_models.TestRun's relevant state information."""
error_info = test_run.GetErrorInfo()
if error_info:
return error_info
if test_run.state == ndb_models.TestRunState.PENDING:
# TODO: consider adding a download progress endpoint instead
for resource in test_run.test_resources:
if resource.cache_url:
continue # already downloaded
tracker = ndb_models.TestResourceTracker.get_by_id(resource.url)
if tracker is None or tracker.completed:
continue # download either not started or already completed
return 'Downloading %s [%d%%]' % (resource.name,
100 * tracker.download_progress)
return None
class TestRunSummary(messages.Message):
"""A test run."""
id = messages.StringField(1)
prev_test_run_id = messages.StringField(2)
labels = messages.StringField(3, repeated=True)
test_name = messages.StringField(4)
device_specs = messages.StringField(5, repeated=True)
state = messages.EnumField(ndb_models.TestRunState, 6)
test_package_info = messages.MessageField(TestPackageInfo, 7)
test_devices = messages.MessageField(TestDeviceInfo, 8, repeated=True)
total_test_count = messages.IntegerField(9)
failed_test_count = messages.IntegerField(10)
failed_test_run_count = messages.IntegerField(11)
create_time = message_types.DateTimeField(12)
update_time = message_types.DateTimeField(13)
@Converter(ndb_models.TestRunSummary, TestRunSummary)
def _TestRunSummaryConverter(obj):
"""Converts a ndb_models.TestRunSummary object to a message."""
return TestRunSummary(
id=str(obj.key.id()),
prev_test_run_id=(str(obj.prev_test_run_key.id())
if obj.prev_test_run_key else None),
labels=obj.labels,
test_name=obj.test_name,
device_specs=obj.device_specs or ConvertToDeviceSpecs(obj.run_target),
state=obj.state,
test_package_info=Convert(obj.test_package_info, TestPackageInfo),
test_devices=ConvertList(obj.test_devices, TestDeviceInfo),
total_test_count=obj.total_test_count,
failed_test_count=obj.failed_test_count,
failed_test_run_count=obj.failed_test_run_count,
create_time=_AddTimezone(obj.create_time),
update_time=_AddTimezone(obj.update_time))
class TestRunSummaryList(messages.Message):
"""A list of test run summaries."""
test_runs = messages.MessageField(TestRunSummary, 1, repeated=True)
prev_page_token = messages.StringField(2)
next_page_token = messages.StringField(3)
class TestRunMetadata(messages.Message):
"""Combination of a test run and its command attempts."""
test_run = messages.MessageField(TestRun, 1, required=True)
command_attempts = messages.MessageField(
api_messages.CommandAttemptMessage, 2, repeated=True)
class TestRunMetadataList(messages.Message):
"""List of test run metadata.
Attributes:
test_runs: a list of test run metadata.
server_version: a server version.
"""
test_runs = messages.MessageField(TestRunMetadata, 1, repeated=True)
server_version = messages.StringField(2)
class RerunContext(messages.Message):
"""Previous test run context used for rerun.
Represents either a local test run (ID provided) or a remote test run (context
file already uploaded to local file storage and filename provided).
"""
test_run_id = messages.StringField(1)
context_filename = messages.StringField(2)
context_file_url = messages.StringField(3)
class NewTestRunRequest(messages.Message):
"""A request object for test_runs.new API."""
labels = messages.StringField(1, repeated=True)
test_run_config = messages.MessageField(TestRunConfig, 2)
rerun_context = messages.MessageField(RerunContext, 3)
rerun_configs = messages.MessageField(TestRunConfig, 4, repeated=True)
required_report_id = messages.StringField(5)
class NodeConfig(messages.Message):
"""Node config."""
env_vars = messages.MessageField(NameValuePair, 1, repeated=True)
test_resource_default_download_urls = messages.MessageField(
NameValuePair, 2, repeated=True)
@Converter(ndb_models.NodeConfig, NodeConfig)
def _NodeConfigConverter(obj):
return NodeConfig(
env_vars=ConvertNameValuePairs(obj.env_vars, NameValuePair),
test_resource_default_download_urls=ConvertNameValuePairs(
obj.test_resource_default_download_urls, NameValuePair))
@Converter(NodeConfig, ndb_models.NodeConfig)
def _NodeConfigMessageConverter(msg):
"""Converts a NodeConfig message into an object."""
return ndb_models.NodeConfig(
id=ndb_models.NODE_CONFIG_ID,
env_vars=ConvertNameValuePairs(msg.env_vars, ndb_models.NameValuePair),
test_resource_default_download_urls=ConvertNameValuePairs(
msg.test_resource_default_download_urls, ndb_models.NameValuePair))
class PrivateNodeConfig(messages.Message):
"""User-specific settings."""
ndb_version = messages.IntegerField(1)
metrics_enabled = messages.BooleanField(2)
gms_client_id = messages.StringField(3)
setup_wizard_completed = messages.BooleanField(4)
default_credentials = messages.MessageField(CredentialsInfo, 5)
@Converter(ndb_models.PrivateNodeConfig, PrivateNodeConfig)
def _PrivateNodeConfigConverter(obj):
return PrivateNodeConfig(
ndb_version=obj.ndb_version,
metrics_enabled=obj.metrics_enabled,
gms_client_id=obj.gms_client_id,
setup_wizard_completed=obj.setup_wizard_completed,
default_credentials=Convert(obj.default_credentials, CredentialsInfo))
@Converter(PrivateNodeConfig, ndb_models.PrivateNodeConfig)
def _PrivateNodeConfigMessageConverter(msg):
"""Converts a PrivateNodeConfig message into an object."""
private_node_config = ndb_models.GetPrivateNodeConfig()
private_node_config.ndb_version = msg.ndb_version
private_node_config.metrics_enabled = msg.metrics_enabled
private_node_config.gms_client_id = msg.gms_client_id
private_node_config.setup_wizard_completed = msg.setup_wizard_completed
return private_node_config
class FileCleanerOperation(messages.Message):
"""File cleaner operation."""
type = messages.EnumField(
ndb_models.FileCleanerOperationType, 1, required=True)
params = messages.MessageField(NameValuePair, 2, repeated=True)
@Converter(ndb_models.FileCleanerOperation, FileCleanerOperation)
def _FileCleanerOperationConverter(obj):
return FileCleanerOperation(
type=obj.type, params=ConvertNameValuePairs(obj.params, NameValuePair))
@Converter(FileCleanerOperation, ndb_models.FileCleanerOperation)
def _FileCleanerOperationMessageConverter(msg):
"""Converts a FileCleanerOperation message into an object."""
return ndb_models.FileCleanerOperation(
type=msg.type,
params=ConvertNameValuePairs(msg.params, ndb_models.NameValuePair))
class FileCleanerCriterion(messages.Message):
"""File cleaner criterion."""
type = messages.EnumField(
ndb_models.FileCleanerCriterionType, 1, required=True)
params = messages.MessageField(NameValuePair, 2, repeated=True)
@Converter(ndb_models.FileCleanerCriterion, FileCleanerCriterion)
def _FileCleanerCriterionConverter(obj):
return FileCleanerCriterion(
type=obj.type, params=ConvertNameValuePairs(obj.params, NameValuePair))
@Converter(FileCleanerCriterion, ndb_models.FileCleanerCriterion)
def _FileCleanerCriterionMessageConverter(msg):
"""Converts a FileCleanerCriterion message into an object."""
return ndb_models.FileCleanerCriterion(
type=msg.type,
params=ConvertNameValuePairs(msg.params, ndb_models.NameValuePair))
class FileCleanerPolicy(messages.Message):
"""File cleaner policy."""
name = messages.StringField(1, required=True)
target = messages.EnumField(ndb_models.FileCleanerTargetType, 2)
operation = messages.MessageField(FileCleanerOperation, 3, required=True)
criteria = messages.MessageField(FileCleanerCriterion, 4, repeated=True)
@Converter(ndb_models.FileCleanerPolicy, FileCleanerPolicy)
def _FileCleanerPolicyConverter(obj):
return FileCleanerPolicy(
name=obj.name,
target=obj.target,
operation=Convert(obj.operation, FileCleanerOperation),
criteria=ConvertList(obj.criteria, FileCleanerCriterion))
@Converter(FileCleanerPolicy, ndb_models.FileCleanerPolicy)
def _FileCleanerPolicyMessageConverter(msg):
"""Converts a FileCleanerPolicy message into an object."""
return ndb_models.FileCleanerPolicy(
name=msg.name,
target=msg.target,
operation=Convert(msg.operation, ndb_models.FileCleanerOperation),
criteria=ConvertList(msg.criteria, ndb_models.FileCleanerCriterion))
class FileCleanerConfig(messages.Message):
"""File cleaner config."""
name = messages.StringField(1, required=True)
description = messages.StringField(2)
directories = messages.StringField(3, repeated=True)
policy_names = messages.StringField(4, repeated=True)
@Converter(ndb_models.FileCleanerConfig, FileCleanerConfig)
def _FileCleanerConfigConverter(obj):
return FileCleanerConfig(
name=obj.name,
description=obj.description,
directories=obj.directories,
policy_names=obj.policy_names)
@Converter(FileCleanerConfig, ndb_models.FileCleanerConfig)
def _FileCleanerConfigMessageConverter(msg):
"""Converts a FileCleanerConfig message into an object."""
return ndb_models.FileCleanerConfig(
name=msg.name,
description=msg.description,
directories=msg.directories,
policy_names=msg.policy_names)
class FileCleanerSettings(messages.Message):
"""File cleaner settings, which combine cleanup policies and configs."""
policies = messages.MessageField(FileCleanerPolicy, 1, repeated=True)
configs = messages.MessageField(FileCleanerConfig, 2, repeated=True)
@Converter(ndb_models.FileCleanerSettings, FileCleanerSettings)
def _FileCleanerSettingsConverter(obj):
return FileCleanerSettings(
policies=ConvertList(obj.policies, FileCleanerPolicy),
configs=ConvertList(obj.configs, FileCleanerConfig))
@Converter(FileCleanerSettings, ndb_models.FileCleanerSettings)
def _FileCleanerSettingsMessageConverter(msg):
"""Converts a FileCleanerSettings message into an object."""
return ndb_models.FileCleanerSettings(
id=ndb_models.FILE_CLEANER_SETTINGS_ID,
policies=ConvertList(msg.policies, ndb_models.FileCleanerPolicy),
configs=ConvertList(msg.configs, ndb_models.FileCleanerConfig))
class NetdataAlarmStatus(messages.Enum):
REMOVED = 0
UNINITIALIZED = 1
UNDEFINED = 2
CLEAR = 3
WARNING = 4
CRITICAL = 5
class NetdataAlarm(messages.Message):
hostname = messages.StringField(1, required=True)
id = messages.IntegerField(2, required=True)
name = messages.StringField(3, required=True)
value = messages.StringField(4, required=True)
status = messages.EnumField(NetdataAlarmStatus, 5, required=True)
class NetdataAlarmList(messages.Message):
alarms = messages.MessageField(NetdataAlarm, 1, repeated=True)
class RequiredReport(messages.Message):
"""A required report."""
id = messages.StringField(1, required=True)
type = messages.EnumField(ndb_models.ReportType, 2)
test_plans = messages.StringField(3, repeated=True)
available = messages.BooleanField(4)
test_run_id = messages.StringField(5)
test_run_state = messages.EnumField(ndb_models.TestRunState, 6)
@Converter(ndb_models.RequiredReport, RequiredReport)
def _RequiredReportConverter(obj):
test_run = obj.test_run_key.get() if obj.test_run_key else None
return RequiredReport(
id=str(obj.key.id()),
type=obj.type,
test_plans=obj.test_plans,
available=obj.available,
test_run_id=str(obj.test_run_key.id()) if obj.test_run_key else None,
test_run_state=test_run.state if test_run else None,
)
class Build(messages.Message):
"""A build."""
id = messages.StringField(1)
name = messages.StringField(2)
fingerprint = messages.StringField(3)
file_url = messages.StringField(4)
size = messages.IntegerField(5)
labels = messages.StringField(6, repeated=True)
create_time = message_types.DateTimeField(7)
update_time = message_types.DateTimeField(8)
detection_status = messages.EnumField(
ndb_models.XtsRequirementsDetectionStatus, 9
)
detection_start_time = message_types.DateTimeField(10)
detection_error_reason = messages.StringField(11)
detection_test_run_id = messages.StringField(12)
required_reports = messages.MessageField(RequiredReport, 13, repeated=True)
@Converter(ndb_models.Build, Build)
def _BuildConverter(obj):
required_reports = list(
ndb_models.RequiredReport.query(
ndb_models.RequiredReport.build_key == obj.key
)
)
return Build(
id=str(obj.key.id()) if obj.key else None,
name=obj.name,
fingerprint=obj.fingerprint,
file_url=obj.file_url,
size=obj.size,
labels=obj.labels,
create_time=_AddTimezone(obj.create_time),
update_time=_AddTimezone(obj.update_time),
detection_status=obj.detection_status,
detection_start_time=_AddTimezone(obj.detection_start_time),
detection_error_reason=obj.detection_error_reason,
detection_test_run_id=str(obj.detection_test_run_key.id())
if obj.detection_test_run_key
else None,
required_reports=ConvertList(required_reports, RequiredReport),
)
class BuildList(messages.Message):
"""A list of builds."""
builds = messages.MessageField(Build, 1, repeated=True)
class XtsRequirementsDetectionRequest(messages.Message):
"""Request to run xTS requirements detection."""
device_spec = messages.StringField(1)
test_resource_objs = messages.MessageField(TestResourceObj, 2, repeated=True)