blob: 84d30d65c2f071f8d2d2eb4c9d95304a57c40c71 [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.
# Messages may be used in api config,
# and we want to set the api parameter camel case.
"""Common config and classes for api."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from protorpc import message_types
from protorpc import messages
import six
from tradefed_cluster import common
# Expose those enum messages so library (in atp switcher) use api_message
# doesn't need to import a sparate common.
CommandErrorType = common.CommandErrorType
CommandState = common.CommandState
CancelReason = common.CancelReason
PredefinedMessageType = common.PredefinedMessageType
RequestState = common.RequestState
HostUpdateState = common.HostUpdateState
# Device serial prefixes used to determine device types.
TCP_DEVICE_PREFIX = "tcp-device"
EMULATOR_DEVICE_PREFIX = "emulator"
NULL_DEVICE_PREFIX = "null-device"
GCE_DEVICE_PREFIX = "gce-device"
REMOTE_DEVICE_PREFIX = "remote-device"
LOCAL_VIRTUAL_DEVICE_PREFIX = "local-virtual-device"
class NonEmptyStringField(messages.StringField):
"""A StringField can not be empty or all whitespace."""
def validate_element(self, value):
"""Validate the value is not empty or all whitespace.
Args:
value: the value of the field
Raises:
ValidationError: value is empty or all whitespace
"""
if isinstance(value, str) or isinstance(value, six.text_type):
if not value.strip():
name = getattr(self, "name")
if not name:
validation_error = messages.ValidationError(
"Field encountered empty string %s" % value)
else:
validation_error = messages.ValidationError(
"Field %s encountered empty string %s" % (name, value))
validation_error.field_name = name
raise validation_error
messages.StringField.validate_element(self, value)
class KeyValuePair(messages.Message):
"""A message class for a key-value pair."""
key = messages.StringField(1, required=True)
value = messages.StringField(2)
class KeyMultiValuePair(messages.Message):
"""A message class for a key-multi value pair."""
key = messages.StringField(1, required=True)
values = messages.StringField(2, repeated=True)
def KeyValuePairMessagesToMap(key_value_pair_messages):
"""Transform a list of KeyValuePair message to a map.
Args:
key_value_pair_messages: a list of KeyValuePair message.
Returns:
a map with a string as key and a string as value
"""
return {msg.key: msg.value for msg in key_value_pair_messages}
def MapToKeyValuePairMessages(key_value_map):
"""Transform a key value map to a list of KeyValuePair message.
Args:
key_value_map: a map with a string as key and a string as value, or None.
Returns:
a list of KeyValuePair message.
"""
key_value_pair_messages = []
if not key_value_map:
return key_value_pair_messages
for k, v in six.iteritems(key_value_map):
if v is not None:
v = _ValueToText(v)
key_value_pair_messages.append(KeyValuePair(key=k, value=v))
key_value_pair_messages.sort(key=lambda p: p.key)
return key_value_pair_messages
def KeyMultiValuePairMessagesToMap(pairs):
"""Transform a list of KeyMultiValuePair message to a dict."""
return {pair.key: pair.values for pair in pairs}
def MapToKeyMultiValuePairMessages(key_values_map):
"""Transform a key-values dict to a list of KeyMultiValuePairs."""
pairs = []
for key, values in six.iteritems((key_values_map or {})):
str_values = [_ValueToText(v) if v is not None else v
for v in values]
pairs.append(KeyMultiValuePair(key=key, values=str_values))
pairs.sort(key=lambda p: p.key)
return pairs
def _ValueToText(value):
"""Convert a value to text."""
if isinstance(value, six.string_types):
return six.ensure_text(value)
return six.ensure_text(repr(value))
class TradefedConfigObjectType(messages.Enum):
"""TF config object types."""
UNKNOWN = 0
TARGET_PREPARER = 1
RESULT_REPORTER = 2
class TradefedConfigObject(messages.Message):
"""A message class for a Tradefed config object."""
type = messages.EnumField(TradefedConfigObjectType, 1, required=True)
class_name = messages.StringField(2, required=True)
option_values = messages.MessageField(KeyMultiValuePair, 3, repeated=True)
class TestEnvironment(messages.Message):
"""A message class to house environment settings for a test request.
Attributes:
env_vars: a dict of environment variables to be set before running a test.
setup_scripts: a list of Linux shell scripts to run before running a test.
(e.g. "sleep 5", "adb reboot")
output_file_patterns: a list of regexes for output file patterns. TFC will
collect these files from test working directoty after a test is
completed.
output_file_upload_url: an URL to which output files will be uploaded. This
can be either a GCS or a HTTP url.
use_subprocess_reporting: a flag on whether to use TF subprocess reporting.
invocation_timeout_millis: millis to wait for a subprocess to finish.
output_idle_timeout_millis: millis to wait for an idle subprocess
jvm_options: a list of JVM options to be passed to TF.
java_properties: a dict of Java properties to be passed to TF.
context_file_pattern: a regex pattern for a test context file which needs to
be passed across attempts (e.g. test_result.xml for xTS).
retry_command_line: a command line to use in retry attempts (optional).
log_level: a log level for a test launcher invocation.
tradefed_config_objects:
a list of TradefedConfigObject to add to a launcher config.
use_parallel_setup: whether to setup devices in parallel.
"""
env_vars = messages.MessageField(KeyValuePair, 1, repeated=True)
setup_scripts = messages.StringField(2, repeated=True)
output_file_patterns = messages.StringField(3, repeated=True)
output_file_upload_url = messages.StringField(4)
use_subprocess_reporting = messages.BooleanField(5)
invocation_timeout_millis = messages.IntegerField(6)
output_idle_timeout_millis = messages.IntegerField(7)
jvm_options = messages.StringField(8, repeated=True)
java_properties = messages.MessageField(KeyValuePair, 9, repeated=True)
context_file_pattern = messages.StringField(10)
extra_context_files = messages.StringField(11, repeated=True)
retry_command_line = messages.StringField(12)
log_level = messages.EnumField(common.LogLevel, 13)
tradefed_config_objects = messages.MessageField(
TradefedConfigObject, 14, repeated=True)
use_parallel_setup = messages.BooleanField(15)
class TestResourceParameters(messages.Message):
"""Repeated properties of TestResource.
Attribtues:
decompress_files: the files to be decompressed from the downloaded file.
"""
decompress_files = messages.StringField(1, repeated=True)
class TestResource(messages.Message):
"""A message class for a test resource.
Attributes:
url: a url to download a test resource from.
name: a filename with which a test resource to be stored in the test working
directory.
path: an option relative path to where a test resource should be downloaded
to.
decompress: whether the host should decompress the downloaded file.
decompress_dir: the directory where the host decompresses the file.
mount_zip: whether to mount a zip file.
params: test resource parameters.
"""
url = messages.StringField(1, required=True)
name = messages.StringField(2)
path = messages.StringField(3)
decompress = messages.BooleanField(4)
decompress_dir = messages.StringField(5)
mount_zip = messages.BooleanField(6)
params = messages.MessageField(TestResourceParameters, 7)
class TestResourceCollection(messages.Message):
"""A message class for a test resource collection."""
test_resources = messages.MessageField(TestResource, 1, repeated=True)
class TestContext(messages.Message):
"""A message class for a text context.
A text context is a data object which gets passed across attempts of a same
command to allow a test invocation to pass information to the next
invocations.
Attributes:
command_line: a command line to run in the next invocation.
env_vars: addtional env. variables.
test_resources: additional test resources.
"""
command_line = messages.StringField(1)
env_vars = messages.MessageField(KeyValuePair, 2, repeated=True)
test_resources = messages.MessageField(TestResource, 3, repeated=True)
class CommandAttemptMessage(messages.Message):
"""Information for a command attempt.
Attributes:
request_id: Request ID for the command this attempt belongs to.
command_id: Command ID this attempt belongs to.
attempt_id: ID of this attempt.
task_id: Task ID for this attempt.
state: State of the attempt. See common.CommandState.
hostname: Host for the device that ran this attempt.
device_serial: Serial for the device that ran this attempt.
start_time: Time when the invocation started.
end_time: Time when the invocation completed.
status: Status from command event for attempt. Used to log exceptions.
error: Error from command event for attempt. User friendly error message.
subprocess_command_error: Error from a subprocess command if exists.
summary: Summary from command event for attempt. Usually a sponge link.
total_test_count: Number of total test case.
failed_test_count: Number of failed test case.
passed_test_count: Number of passed test case.
create_time: Time of the first command event processed for this attempt.
update_time: Time of the last command event processed for this attempt.
error_reason: Error reason get from error. Informative message.
error_type: Error type get from error. See common.CommandErrorType.
failed_test_run_count: Number of failed test run.
device_serials: a list of device serials that the command attempt uses.
device_lost_detected: Number of devices lost.
run_index: run index from [0, run_count). The (run_index, attempt_index)
tuple should be unique for a given command.
attempt_index: attempt index. The (run_index, attempt_index) tuple should be
unique for a given command.
"""
request_id = messages.StringField(1, required=True)
command_id = messages.StringField(2, required=True)
attempt_id = messages.StringField(3, required=True)
task_id = messages.StringField(4, required=True)
state = messages.EnumField(common.CommandState, 5)
hostname = messages.StringField(6)
# TODO Deprecated.
device_serial = messages.StringField(7)
start_time = message_types.DateTimeField(8)
end_time = message_types.DateTimeField(9)
status = messages.StringField(10)
error = messages.StringField(11)
subprocess_command_error = messages.StringField(12)
summary = messages.StringField(13)
total_test_count = messages.IntegerField(14)
failed_test_count = messages.IntegerField(15)
passed_test_count = messages.IntegerField(16)
create_time = message_types.DateTimeField(17)
update_time = message_types.DateTimeField(18)
error_reason = messages.StringField(19)
error_type = messages.EnumField(common.CommandErrorType, 20)
failed_test_run_count = messages.IntegerField(21)
device_serials = messages.StringField(22, repeated=True)
device_lost_detected = messages.IntegerField(23)
run_index = messages.IntegerField(24)
attempt_index = messages.IntegerField(25)
class CommandAttemptMessageCollection(messages.Message):
"""A class representing a collection of command attempts."""
command_attempts = messages.MessageField(CommandAttemptMessage, 1,
repeated=True)
class CommandAttemptEventMessage(messages.Message):
"""A class representing a command attempt event.
This is used in notifier when publishing command attempt events.
Attributes:
type: an attempt event type string
attempt: a command attempt message
old_state: an enum type command state
new_state: an enum type command state
event_time: a datetime when the event occurred
"""
type = messages.StringField(1)
attempt = messages.MessageField(CommandAttemptMessage, 2)
old_state = messages.EnumField(common.CommandState, 3)
new_state = messages.EnumField(common.CommandState, 4)
event_time = message_types.DateTimeField(5)
class DeviceAttributeRequirement(messages.Message):
"""Device attribute requirement."""
name = messages.StringField(1)
value = messages.StringField(2)
operator = messages.StringField(3)
class RunTargetRequirement(messages.Message):
"""Run target requirement."""
name = messages.StringField(1)
device_attributes = messages.MessageField(
DeviceAttributeRequirement, 2, repeated=True)
class GroupRequirement(messages.Message):
"""A mutual exclusive group defined on TF hosts."""
run_targets = messages.MessageField(RunTargetRequirement, 1, repeated=True)
class HostRequirement(messages.Message):
"""Host requirement."""
groups = messages.MessageField(GroupRequirement, 1, repeated=True)
class TestBenchRequirement(messages.Message):
"""Test bench requirement for a test."""
cluster = messages.StringField(1)
host = messages.MessageField(HostRequirement, 2)
class CommandInfo(messages.Message):
"""A command info message.
Attributes:
name: an optional human-friendly name for a command.
command_line: a command line.
cluster: a cluster.
run_target: a run target.
run_count: a run count.
shard_count: a shard count.
allow_partial_device_match: a boolean field indicating whether partial
device match is supported or not.
"""
name = messages.StringField(1)
command_line = NonEmptyStringField(2)
cluster = NonEmptyStringField(3)
run_target = NonEmptyStringField(4)
run_count = messages.IntegerField(5, default=1)
shard_count = messages.IntegerField(6, default=1)
allow_partial_device_match = messages.BooleanField(7, default=False)
test_bench = messages.MessageField(TestBenchRequirement, 8)
test_bench_attributes = messages.StringField(9, repeated=True)
class CommandMessage(messages.Message):
"""Information for a Command."""
id = messages.StringField(1, required=True)
request_id = messages.StringField(2, required=True)
command_line = messages.StringField(3, required=True)
cluster = messages.StringField(4)
run_target = messages.StringField(5)
run_count = messages.IntegerField(6)
shard_count = messages.IntegerField(7)
shard_index = messages.IntegerField(8)
state = messages.EnumField(common.CommandState, 9)
start_time = message_types.DateTimeField(10)
end_time = message_types.DateTimeField(11)
create_time = message_types.DateTimeField(12)
update_time = message_types.DateTimeField(13)
cancel_reason = messages.EnumField(common.CancelReason, 14)
error_reason = messages.EnumField(common.ErrorReason, 15)
name = messages.StringField(16)
allow_partial_device_match = messages.BooleanField(17, default=False)
test_bench = messages.MessageField(TestBenchRequirement, 18)
affinity_tag = messages.StringField(19)
total_test_count = messages.IntegerField(20)
failed_test_count = messages.IntegerField(21)
passed_test_count = messages.IntegerField(22)
failed_test_run_count = messages.IntegerField(23)
class CommandMessageCollection(messages.Message):
"""A class representing a collection of commands."""
commands = messages.MessageField(CommandMessage, 1, repeated=True)
page_token = messages.StringField(2)
class CommandStateStat(messages.Message):
"""The number of commands for a given state."""
state = messages.EnumField(common.CommandState, 1)
count = messages.IntegerField(2)
class CommandStateStats(messages.Message):
"""Lists the number of commands for each state."""
state_stats = messages.MessageField(CommandStateStat, 1, repeated=True)
create_time = message_types.DateTimeField(2)
class RequestType(messages.Enum):
"""Request types."""
UNMANAGED = 0 # An unmanaged request
MANAGED = 1 # A request for which TFC/TF handles setup/teardown process.
class RequestMessage(messages.Message):
"""A class representing a test request.
TODO: add attributes the frontend needed to this class
Attributes:
id: a request ID.
user: email of an user who made the request.
command_infos: a list of command infos.
priority: a priority value. Should be in range [0-1000]. Higher number means
higher priority.
queue_timeout_seconds: a queue timeout in seconds. A request will time out
if it stays in QUEUED state longer than a given timeout.
cancel_reason: a enum cancel reason.
max_retry_on_test_failures: the max number of retries on test failure per
each command.
max_concurrent_tasks: the max number of concurrent tasks.
prev_test_context: a previous test context.
state: a state of the request.
start_time: test execution start time.
end_time: test execution stop time.
create_time: time when the request was created.
update_time: time when the request was last updated.
command_attempts: a list of CommandAttemptMessages.
cancel_message: a cancel message.
api_module_version: API module version.
commands: a list of commands of the request.
command_attempts: a list of command attempts of the request.
command_line: a command line (Deprecated).
cluster: a target cluster name (Deprecated).
run_target: a run target (Deprecated).
run_count: a run count (Deprecated).
shard_count: a shard count (Deprecated).
"""
id = messages.StringField(1)
type = messages.EnumField(RequestType, 2)
user = messages.StringField(3)
command_infos = messages.MessageField(CommandInfo, 4, repeated=True)
priority = messages.IntegerField(5)
queue_timeout_seconds = messages.IntegerField(6)
cancel_reason = messages.EnumField(common.CancelReason, 7)
max_retry_on_test_failures = messages.IntegerField(8)
prev_test_context = messages.MessageField(TestContext, 9)
max_concurrent_tasks = messages.IntegerField(10)
affinity_tag = messages.StringField(25)
state = messages.EnumField(common.RequestState, 11)
start_time = message_types.DateTimeField(12)
end_time = message_types.DateTimeField(13)
create_time = message_types.DateTimeField(14)
update_time = message_types.DateTimeField(15)
# TODO: Deprecate cancel_message after remove the usage in ATP.
cancel_message = messages.StringField(16)
api_module_version = messages.StringField(17)
commands = messages.MessageField(
CommandMessage, 18, repeated=True)
command_attempts = messages.MessageField(
CommandAttemptMessage, 19, repeated=True)
# Deprecated fields
# TODO: remove after updating dependent services.
command_line = messages.StringField(20)
cluster = messages.StringField(21)
run_target = messages.StringField(22)
run_count = messages.IntegerField(23)
shard_count = messages.IntegerField(24)
# Retry information that belongs to the same test run.
next_attempt_session_id = messages.StringField(26)
previous_attempt_session_ids = messages.StringField(27, repeated=True)
class RequestMessageCollection(messages.Message):
"""A class representing a collection of test requests."""
requests = messages.MessageField(RequestMessage, 1, repeated=True)
class RequestEventMessage(messages.Message):
"""A class representing a test request event.
Attributes:
type: a request event type string.
request_id: a request ID.
new_state: a enum type new state.
request: a request obejct.
summary: concatention of summaries in all attempts.
total_test_count: total test count.
failed_test_count: failed test count.
passed_test_count: passed test count.
result_links: result links for all attempts.
total_run_time_sec: total run time seconds
error_reason: first error reason string in all attempts.
error_type: first error type enum in all attempts.
failed_test_run_count: failed test run count.
device_lost_detected: Number of devices lost.
This is used in notifier when publishing request events.
"""
type = messages.StringField(1)
request_id = messages.StringField(2)
new_state = messages.EnumField(common.RequestState, 3)
request = messages.MessageField(RequestMessage, 4)
summary = messages.StringField(5)
total_test_count = messages.IntegerField(6)
failed_test_count = messages.IntegerField(7)
passed_test_count = messages.IntegerField(8)
result_links = messages.StringField(9, repeated=True)
total_run_time_sec = messages.IntegerField(10)
error_reason = messages.StringField(11)
error_type = messages.EnumField(common.CommandErrorType, 12)
event_time = message_types.DateTimeField(13)
failed_test_run_count = messages.IntegerField(14)
device_lost_detected = messages.IntegerField(15)
class Note(messages.Message):
"""Note for a cluster, host, or device."""
id = messages.StringField(1)
user = messages.StringField(2)
timestamp = message_types.DateTimeField(3)
message = messages.StringField(4)
offline_reason = messages.StringField(5)
recovery_action = messages.StringField(6)
type = messages.EnumField(common.NoteType, 7)
cluster_id = messages.StringField(8)
hostname = messages.StringField(9)
device_serial = messages.StringField(10)
event_time = message_types.DateTimeField(11)
class NoteCollection(messages.Message):
"""A class representing a collection of notes."""
notes = messages.MessageField(Note, 1, repeated=True)
more = messages.BooleanField(2)
next_cursor = messages.StringField(3)
prev_cursor = messages.StringField(4)
class NoteEvent(messages.Message):
"""Note event with part of the cluster/host/device info message."""
note = messages.MessageField(Note, 1, required=True)
cluster_id = messages.StringField(2)
hostname = messages.StringField(3)
lab_name = messages.StringField(4)
run_target = messages.StringField(5)
publish_timestamp = message_types.DateTimeField(6, required=True)
class BatchUpdateNotesWithPredefinedMessageRequest(messages.Message):
"""An object that contains the request body of batch update notes endpoint."""
notes = messages.MessageField(Note, 1, repeated=True)
message = messages.StringField(2)
offline_reason = messages.StringField(3)
recovery_action = messages.StringField(4)
offline_reason_id = messages.IntegerField(5)
recovery_action_id = messages.IntegerField(6)
lab_name = messages.StringField(7)
user = messages.StringField(8)
event_time = message_types.DateTimeField(9)
class DeviceStateHistory(messages.Message):
"""Device state history record."""
timestamp = message_types.DateTimeField(1, required=True)
state = messages.StringField(2, required=True)
class DeviceTypeMessage(messages.Enum):
"""Device types."""
EMULATOR = 0
TCP = 1
NULL = 2
PHYSICAL = 3
GCE = 4
REMOTE = 5
LOCAL_VIRTUAL = 6
REMOTE_VIRTUAL = 7
def GetDeviceType(serial, is_stub_device=False, preconfigured_ip=None):
"""Helper to get a device type from a serial.
Args:
serial: Device serial
is_stub_device: If the device is a stub device
preconfigured_ip: The preconfigured ip for the device
Returns:
A DeviceTypeMessage for the given information
"""
# Return REMOTE_VIRTUAL if the device has a preconfigured stub
if is_stub_device and preconfigured_ip:
return DeviceTypeMessage.REMOTE_VIRTUAL
# If a serial has a host prefix, remove it.
if ":" in serial:
serial = serial.split(":", 2)[1]
if serial.startswith(EMULATOR_DEVICE_PREFIX):
return DeviceTypeMessage.EMULATOR
if serial.startswith(TCP_DEVICE_PREFIX):
return DeviceTypeMessage.TCP
if serial.startswith(NULL_DEVICE_PREFIX):
return DeviceTypeMessage.NULL
if serial.startswith(GCE_DEVICE_PREFIX):
return DeviceTypeMessage.GCE
if serial.startswith(REMOTE_DEVICE_PREFIX):
return DeviceTypeMessage.REMOTE
if serial.startswith(LOCAL_VIRTUAL_DEVICE_PREFIX):
return DeviceTypeMessage.LOCAL_VIRTUAL
return DeviceTypeMessage.PHYSICAL
class DeviceInfo(messages.Message):
"""Information for a given test device.
TODO: Create an extra_info field for data not intended to be queried
individually (eg. SIM information).
Attributes:
device_serial: Serial identifying the device. It should be unique.
According to @bgay, it is a rare occurrence and non-unique device serials
are usually rejected.
lab_name: the name of the lab this device belong to.
hostname: The name of the host this device is connected to.
run_target: Run target for the device.
build_id: Current build ID in the device.
product: Device product (Eg.: flounder).
product_variant: Device product variant (Eg.: flounder_lte)
sdk_version: SDK version of the device's build.
state: Reported state of the device.
battery_level: Reported battery level of the device.
hidden: Is the device hidden.
notes: Notes entered by a user.
history: State history.
utilization: Rate of allocated over total time of the device.
cluster: Cluster ID for this device's host.
host_group: a group of host using the same host config.
pools: pools of devices to manage device resources.
mac_address: MAC address of the device.
group_name: Device group name.
sim_state: State of the SIM.
sim_operator: Operator of the SIM.
extra_info: a key value list for device's extra info.
flated_extra_info: a list representation of extra_info,
each element is "key=value".
test_harness: test harness the device is running under.
recovery_state: recovery state for the host, e.g. assigned, fixed, verified.
last_recovery_time: the last time the device gets recovered.
is_stub_device: specifies if the device is a stub device.
display_serial: the serial used for display purpose.
preconfigured_ip: the preconfigured ip if the device is on a remote host.
preconfigured_device_num_offset: the preconfigured device number offset.
"""
device_serial = messages.StringField(1)
lab_name = messages.StringField(2)
hostname = messages.StringField(3)
run_target = messages.StringField(4)
build_id = messages.StringField(5)
product = messages.StringField(6)
product_variant = messages.StringField(7)
sdk_version = messages.StringField(8)
state = messages.StringField(9)
timestamp = message_types.DateTimeField(10)
battery_level = messages.StringField(11)
hidden = messages.BooleanField(12)
notes = messages.MessageField(Note, 13, repeated=True)
history = messages.MessageField(DeviceStateHistory, 14, repeated=True)
utilization = messages.FloatField(15)
# TODO: deprecate physical_cluster, use host_group.
cluster = messages.StringField(16)
host_group = messages.StringField(17)
pools = messages.StringField(18, repeated=True)
device_type = messages.EnumField(DeviceTypeMessage, 19)
mac_address = messages.StringField(20)
group_name = messages.StringField(21)
sim_state = messages.StringField(22)
sim_operator = messages.StringField(23)
extra_info = messages.MessageField(KeyValuePair, 24, repeated=True)
# TODO: Change to flattened_extra_info.
flated_extra_info = messages.StringField(25, repeated=True)
test_harness = messages.StringField(26)
recovery_state = messages.StringField(27)
last_recovery_time = message_types.DateTimeField(28)
is_stub_device = messages.BooleanField(29)
display_serial = messages.StringField(30)
preconfigured_ip = messages.StringField(31)
preconfigured_device_num_offset = messages.IntegerField(32)
class DeviceInfoCollection(messages.Message):
"""A class representing a collection of host infos."""
device_infos = messages.MessageField(DeviceInfo, 1, repeated=True)
# The cursor after the last device entity.
# This is the start cursor for the next batch query.
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
# There are more entities after or not.
# TODO: remove more, use next_cursor should be enough.
more = messages.BooleanField(4)
class DeviceInfoHistoryCollection(messages.Message):
"""A class representing a collection of device histories."""
histories = messages.MessageField(DeviceInfo, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
class HostState(messages.Enum):
"""Enum for host states."""
UNKNOWN = 0
GONE = 1
RUNNING = 2
QUITTING = 3
KILLING = 4
class HostStateHistory(messages.Message):
"""Host state history."""
hostname = messages.StringField(1, required=True)
timestamp = message_types.DateTimeField(2, required=True)
state = messages.StringField(3, required=True)
class HostConfig(messages.Message):
"""Information for a given host config."""
hostname = messages.StringField(1)
tf_global_config_path = messages.StringField(2)
host_login_name = messages.StringField(3)
lab_name = messages.StringField(4)
cluster_name = messages.StringField(5)
graceful_shutdown = messages.BooleanField(6)
shutdown_timeout_sec = messages.IntegerField(7)
enable_ui_update = messages.BooleanField(8)
owners = messages.StringField(9, repeated=True)
inventory_groups = messages.StringField(10, repeated=True)
docker_image = messages.StringField(11)
class HostConfigCollection(messages.Message):
"""Collection of HostConfigs."""
host_configs = messages.MessageField(HostConfig, 1, repeated=True)
next_cursor = messages.StringField(2)
class HostMetadata(messages.Message):
"""Host metadata."""
hostname = messages.StringField(1)
test_harness_image = messages.StringField(2)
update_time = message_types.DateTimeField(3)
allow_to_update = messages.BooleanField(4)
class HostResource(messages.Message):
"""Host resource."""
hostname = messages.StringField(1)
resource = messages.StringField(2)
update_timestamp = message_types.DateTimeField(3)
event_timestamp = message_types.DateTimeField(4)
class DeviceCountSummary(messages.Message):
"""Information of device count for a host."""
run_target = messages.StringField(1)
total = messages.IntegerField(2)
offline = messages.IntegerField(3)
available = messages.IntegerField(4)
allocated = messages.IntegerField(5)
timestamp = message_types.DateTimeField(6)
class HostInfo(messages.Message):
"""Information for a given test host."""
hostname = messages.StringField(1)
lab_name = messages.StringField(2)
# TODO: deprecate physical_cluster, use host_group.
cluster = messages.StringField(3)
host_group = messages.StringField(4)
# TODO: deprecated test runner and test runner version.
test_runner = messages.StringField(5)
test_runner_version = messages.StringField(6)
device_infos = messages.MessageField(DeviceInfo, 7, repeated=True)
timestamp = message_types.DateTimeField(8)
total_devices = messages.IntegerField(9)
offline_devices = messages.IntegerField(10)
available_devices = messages.IntegerField(11)
allocated_devices = messages.IntegerField(12)
device_count_timestamp = message_types.DateTimeField(13)
hidden = messages.BooleanField(14)
notes = messages.MessageField(Note, 15, repeated=True)
extra_info = messages.MessageField(KeyValuePair, 16, repeated=True)
# TODO: deprecate clusters, use pools.
next_cluster_ids = messages.StringField(17, repeated=True)
pools = messages.StringField(18, repeated=True)
host_state = messages.StringField(19)
state_history = messages.MessageField(HostStateHistory, 20, repeated=True)
assignee = messages.StringField(21)
device_count_summaries = messages.MessageField(
DeviceCountSummary, 22, repeated=True)
# Bad host is defined in device_monitor._UpdateHostBadness.
is_bad = messages.BooleanField(23)
test_harness = messages.StringField(24)
test_harness_version = messages.StringField(25)
flated_extra_info = messages.StringField(26, repeated=True)
last_recovery_time = message_types.DateTimeField(27)
recovery_state = messages.StringField(28)
update_state = messages.StringField(29)
update_state_display_message = messages.StringField(30)
bad_reason = messages.StringField(31)
update_timestamp = message_types.DateTimeField(32)
class HostInfoCollection(messages.Message):
"""A class representing a collection of host infos."""
host_infos = messages.MessageField(HostInfo, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
# TODO: remove more, use next_cursor should be enough
more = messages.BooleanField(4)
class HostInfoHistoryCollection(messages.Message):
"""A class representing a collection of host histories."""
histories = messages.MessageField(HostInfo, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
class RunTarget(messages.Message):
"""Run target message class."""
name = messages.StringField(1, required=True)
class HostUpdateStateSummary(messages.Message):
"""Host update state summary."""
total = messages.IntegerField(1)
unknown = messages.IntegerField(2)
pending = messages.IntegerField(3)
syncing = messages.IntegerField(4)
shutting_down = messages.IntegerField(5)
restarting = messages.IntegerField(6)
timed_out = messages.IntegerField(7)
errored = messages.IntegerField(8)
succeeded = messages.IntegerField(9)
update_timestamp = message_types.DateTimeField(10)
target_version = messages.StringField(11)
class ClusterInfo(messages.Message):
"""Information for a given Cluster."""
cluster_id = messages.StringField(1)
total_devices = messages.IntegerField(2)
offline_devices = messages.IntegerField(3)
available_devices = messages.IntegerField(4)
allocated_devices = messages.IntegerField(5)
device_count_timestamp = message_types.DateTimeField(6)
host_infos = messages.MessageField(HostInfo, 7, repeated=True)
notes = messages.MessageField(Note, 8, repeated=True)
run_targets = messages.MessageField(RunTarget, 9, repeated=True)
host_update_state_summary = messages.MessageField(HostUpdateStateSummary, 10)
host_count_by_harness_version = messages.MessageField(
KeyValuePair, 11, repeated=True)
host_update_state_summaries_by_version = messages.MessageField(
HostUpdateStateSummary, 12, repeated=True)
class LabInfo(messages.Message):
"""Information for a given Lab."""
lab_name = messages.StringField(1)
update_timestamp = message_types.DateTimeField(2)
owners = messages.StringField(3, repeated=True)
host_update_state_summary = messages.MessageField(HostUpdateStateSummary, 4)
host_count_by_harness_version = messages.MessageField(
KeyValuePair, 5, repeated=True)
host_update_state_summaries_by_version = messages.MessageField(
HostUpdateStateSummary, 6, repeated=True)
class LabInfoCollection(messages.Message):
"""A class representing a collection of lab infos."""
lab_infos = messages.MessageField(LabInfo, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
# TODO: remove more, use next_cursor should be enough
more = messages.BooleanField(4)
class AclCheckResult(messages.Message):
"""A message indicating if the client hass access or not."""
has_access = messages.BooleanField(1, default=False)
class CheckAdminMessage(messages.Message):
"""A message class for admin check."""
isAdmin = messages.BooleanField(1, default=False)
class NewRequestMessage(messages.Message):
"""A message class for parameters to create new request."""
type = messages.EnumField(RequestType, 1)
user = NonEmptyStringField(2)
command_line = NonEmptyStringField(3)
priority = messages.IntegerField(4)
queue_timeout_seconds = messages.IntegerField(5)
cluster = NonEmptyStringField(6)
run_target = NonEmptyStringField(7)
run_count = messages.IntegerField(8, default=1)
shard_count = messages.IntegerField(9, default=1)
max_retry_on_test_failures = messages.IntegerField(10)
max_concurrent_tasks = messages.IntegerField(11)
prev_test_context = messages.MessageField(TestContext, 12)
test_environment = messages.MessageField(TestEnvironment, 13)
test_resources = messages.MessageField(TestResource, 14, repeated=True)
plugin_data = messages.MessageField(KeyValuePair, 15, repeated=True)
test_bench_attributes = messages.StringField(16, repeated=True)
# TODO: deprecated cluster, run target and test_bench_attributes
# use test_bench object instead.
test_bench = messages.MessageField(TestBenchRequirement, 17)
affinity_tag = messages.StringField(18)
class NewMultiCommandRequestMessage(messages.Message):
"""A message class for parameters to create new request.
Attributes:
type: a request type.
user: a user ID.
command_infos: infos on commands to run.
priority: a priority of a request.
queue_timeout_seconds: a queue timeout in seconds.
max_retry_on_test_failures: a max number of retries on test failures.
max_concurrent_tasks: a max number of concurrent tasks.
prev_test_context: a previous test context. Only used for managed requests.
test_environment: a test environment. Only used for managed requests.
test_resources: a list of test resources. Only used for managed
requests.
plugin_data: plugin data.
affinity_tag: an optional affinity tag.
"""
type = messages.EnumField(RequestType, 1)
user = NonEmptyStringField(2)
command_infos = messages.MessageField(CommandInfo, 3, repeated=True)
priority = messages.IntegerField(4)
queue_timeout_seconds = messages.IntegerField(5)
max_retry_on_test_failures = messages.IntegerField(6)
max_concurrent_tasks = messages.IntegerField(7)
prev_test_context = messages.MessageField(TestContext, 8)
test_environment = messages.MessageField(TestEnvironment, 9)
test_resources = messages.MessageField(TestResource, 10, repeated=True)
plugin_data = messages.MessageField(KeyValuePair, 11, repeated=True)
affinity_tag = messages.StringField(12)
class CommandEventType(messages.Enum):
"""The different types of command events."""
FETCH_FAILED = 1
EXECUTE_FAILED = 2
INVOCATION_STARTED = 3
INVOCATION_FAILED = 4
INVOCATION_ENDED = 5
INVOCATION_COMPLETED = 6
TEST_RUN_STARTED = 7
TEST_RUN_ENDED = 8
TEST_ENDED = 9
class CommandEventData(messages.Message):
"""Extra data included in a command event."""
total_test_count = messages.IntegerField(1)
exec_test_count = messages.IntegerField(2)
class DeviceNote(messages.Message):
"""Device note message."""
id = messages.StringField(1, required=True)
device_serial = messages.StringField(2, required=True)
user = messages.StringField(3)
update_timestamp = message_types.DateTimeField(4)
offline_reason = messages.StringField(5)
recovery_action = messages.StringField(6)
message = messages.StringField(7)
class DeviceNoteEvent(messages.Message):
"""Device note event with part of the device info message."""
device_note = messages.MessageField(DeviceNote, 1, required=True)
hostname = messages.StringField(2, required=True)
lab_name = messages.StringField(3)
run_target = messages.StringField(4)
publish_timestamp = message_types.DateTimeField(5, required=True)
class DeviceNoteCollection(messages.Message):
"""A class representing a collection of device notes."""
device_notes = messages.MessageField(DeviceNote, 1, repeated=True)
# TODO: remove more, use next_cursor should be enough
more = messages.BooleanField(2)
next_cursor = messages.StringField(3)
prev_cursor = messages.StringField(4)
class HostNote(messages.Message):
"""Host note message."""
id = messages.StringField(1, required=True)
hostname = messages.StringField(2, required=True)
user = messages.StringField(3)
update_timestamp = message_types.DateTimeField(4)
offline_reason = messages.StringField(5)
recovery_action = messages.StringField(6)
message = messages.StringField(7)
class HostNoteEvent(messages.Message):
"""Host note event message."""
host_note = messages.MessageField(HostNote, 1, required=True)
lab_name = messages.StringField(2)
publish_timestamp = message_types.DateTimeField(3, required=True)
class HostNoteCollection(messages.Message):
"""A class representing a collection of host notes."""
host_notes = messages.MessageField(HostNote, 1, repeated=True)
# TODO: remove more, use next_cursor should be enough
more = messages.BooleanField(2)
next_cursor = messages.StringField(3)
prev_cursor = messages.StringField(4)
class PredefinedMessage(messages.Message):
"""Predefined messages that describe incidents.
Attributes:
id: int, the id of a PredefinedMessage.
lab_name: lab_name, the lab that the predefined_message belongs to.
type: the type of the message.
content: a unique text content of the message.
create_timestamp: the datetime that the message is first created.
used_count: the count the message has been used.
"""
id = messages.IntegerField(1, required=True)
lab_name = messages.StringField(2, required=True)
type = messages.EnumField(PredefinedMessageType, 3, required=True)
content = messages.StringField(4, required=True)
create_timestamp = message_types.DateTimeField(5)
used_count = messages.IntegerField(6, default=0)
class PredefinedMessageCollection(messages.Message):
"""A class representing a collection of predefined messages."""
predefined_messages = messages.MessageField(
PredefinedMessage, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
class TestGroupStatus(messages.Message):
"""A message class to store test group status.
Attributes:
name: a test group name.
total_test_count: total number of tests.
completed_test_count: number of completed tests.
failed_test_count: number of failed tests.
passed_test_count: number of passed tests.
is_complete: a flag indicating completion of a test module.
elapsed_time: elapsed time in millis
failure_message: a failure message.
"""
name = messages.StringField(1)
total_test_count = messages.IntegerField(2)
completed_test_count = messages.IntegerField(3)
failed_test_count = messages.IntegerField(4)
passed_test_count = messages.IntegerField(5)
is_complete = messages.BooleanField(6)
elapsed_time = messages.IntegerField(7)
failure_message = messages.StringField(8)
class InvocationStatus(messages.Message):
test_group_statuses = messages.MessageField(TestGroupStatus, 1, repeated=True)
class CommandEvent(messages.Message):
"""A message class representing a cluster command event.
TODO: 'event_type' field is not being used. Coordinator uses the
'type' field for command event types.
"""
event_type = messages.EnumField(CommandEventType, 1)
time = messages.IntegerField(2)
task_id = messages.StringField(3)
attempt_id = messages.StringField(4)
hostname = messages.StringField(5)
# TODO Deprecated.
device_serial = messages.StringField(6)
data = messages.MessageField(CommandEventData, 7)
type = messages.StringField(8)
invocation_status = messages.MessageField(InvocationStatus, 9)
device_serials = messages.StringField(10, repeated=True)
class CommandEventList(messages.Message):
"""A message class representing a list of cluster command events."""
command_events = messages.MessageField(CommandEvent, 1, repeated=True)
class FilterHintMessage(messages.Message):
"""A message class representing filter hint."""
value = messages.StringField(1)
class FilterHintCollection(messages.Message):
"""A class representing a collection of filter hint."""
filter_hints = messages.MessageField(FilterHintMessage, 1, repeated=True)
class DeviceBlocklistMessage(messages.Message):
"""Device blocklist message."""
key_id = messages.IntegerField(1)
lab_name = messages.StringField(2)
# TODO: Add other fields, e.g. cluster, hostname, etc.
create_timestamp = message_types.DateTimeField(3)
note = messages.StringField(4)
user = messages.StringField(5)
class DeviceBlocklistCollection(messages.Message):
"""A list of device block lists."""
device_blocklists = messages.MessageField(
DeviceBlocklistMessage, 1, repeated=True)
next_cursor = messages.StringField(2)
prev_cursor = messages.StringField(3)
class DeviceBlocklistArchiveMessage(messages.Message):
"""Device blocklist archive message."""
device_blocklist = messages.MessageField(DeviceBlocklistMessage, 1)
start_timestamp = message_types.DateTimeField(2)
end_timestamp = message_types.DateTimeField(3)
archived_by = messages.StringField(4)
class HostRecoveryStateRequest(messages.Message):
"""Host recovery state request."""
hostname = messages.StringField(1, required=True)
recovery_state = messages.StringField(2, required=True)
assignee = messages.StringField(3)
class HostRecoveryStateRequests(messages.Message):
"""A list of host recovery state request."""
host_recovery_state_requests = messages.MessageField(
HostRecoveryStateRequest, 1, repeated=True)
class DeviceRecoveryStateRequest(messages.Message):
"""Device recovery state request."""
hostname = messages.StringField(1)
device_serial = messages.StringField(2, required=True)
recovery_state = messages.StringField(3, required=True)
assignee = messages.StringField(4)
class DeviceRecoveryStateRequests(messages.Message):
"""A list of device recovery state request."""
device_recovery_state_requests = messages.MessageField(
DeviceRecoveryStateRequest, 1, repeated=True)
class TestHarnessImageMetadataMessage(messages.Message):
"""Test harness image metadata message."""
repo_name = messages.StringField(1)
digest = messages.StringField(2)
test_harness = messages.StringField(3)
test_harness_version = messages.StringField(4)
create_time = message_types.DateTimeField(5)
tags = messages.StringField(9, repeated=True)
class TestHarnessImageMetadataCollection(messages.Message):
"""A collection of test harness image metadata."""
images = messages.MessageField(
TestHarnessImageMetadataMessage, 1, repeated=True)
next_cursor = messages.StringField(2)
class CheckResourcePermissionRequest(messages.Message):
"""Request for checking user access permission.
Attributes:
resource_type: what kind of resource to be checked,
could be "host" or "device".
permission: what kind of permission to be checked,
could be "owner" or "reader".
user_name: the user name.
resource_id: the target object id. Depends on the "resource" field,
it could be hostname or device_serial.
"""
resource_type = messages.StringField(1, required=True)
permission = messages.StringField(2, required=True)
user_name = messages.StringField(3, required=True)
resource_id = messages.StringField(4, required=True)