Basic DUT topology for multidut

This is the proto payload that will be provided directly to the test
harnesses for execution.

BUG=None
TEST=generate

Change-Id: I52000d72129ac99edbee51b0caec7f2afa00aa71
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/config/+/2973317
Auto-Submit: C Shapiro <shapiroc@chromium.org>
Reviewed-by: Jaques Clapauch <jaquesc@google.com>
Commit-Queue: Jaques Clapauch <jaquesc@google.com>
diff --git a/go/test/lab/api/dut.pb.go b/go/test/lab/api/dut.pb.go
index caded73..7f7562b 100644
--- a/go/test/lab/api/dut.pb.go
+++ b/go/test/lab/api/dut.pb.go
@@ -23,13 +23,14 @@
 
 // Specification of Device Under Test.
 type Dut struct {
-	// Manufacturing hardware design/component details for a given device
-	MfgConfig *api.MfgConfig `protobuf:"bytes,2,opt,name=mfg_config,json=mfgConfig,proto3" json:"mfg_config,omitempty"`
-	// Endpoint for ssh service running on the device
-	Ssh                  *IpEndpoint `protobuf:"bytes,3,opt,name=ssh,proto3" json:"ssh,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	Id *Dut_Id `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	// Types that are valid to be assigned to DutType:
+	//	*Dut_Chromeos
+	//	*Dut_Android_
+	DutType              isDut_DutType `protobuf_oneof:"dut_type"`
+	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
+	XXX_unrecognized     []byte        `json:"-"`
+	XXX_sizecache        int32         `json:"-"`
 }
 
 func (m *Dut) Reset()         { *m = Dut{} }
@@ -57,20 +58,58 @@
 
 var xxx_messageInfo_Dut proto.InternalMessageInfo
 
-func (m *Dut) GetMfgConfig() *api.MfgConfig {
+func (m *Dut) GetId() *Dut_Id {
 	if m != nil {
-		return m.MfgConfig
+		return m.Id
 	}
 	return nil
 }
 
-func (m *Dut) GetSsh() *IpEndpoint {
+type isDut_DutType interface {
+	isDut_DutType()
+}
+
+type Dut_Chromeos struct {
+	Chromeos *Dut_ChromeOS `protobuf:"bytes,2,opt,name=chromeos,proto3,oneof"`
+}
+
+type Dut_Android_ struct {
+	Android *Dut_Android `protobuf:"bytes,3,opt,name=android,proto3,oneof"`
+}
+
+func (*Dut_Chromeos) isDut_DutType() {}
+
+func (*Dut_Android_) isDut_DutType() {}
+
+func (m *Dut) GetDutType() isDut_DutType {
 	if m != nil {
-		return m.Ssh
+		return m.DutType
 	}
 	return nil
 }
 
+func (m *Dut) GetChromeos() *Dut_ChromeOS {
+	if x, ok := m.GetDutType().(*Dut_Chromeos); ok {
+		return x.Chromeos
+	}
+	return nil
+}
+
+func (m *Dut) GetAndroid() *Dut_Android {
+	if x, ok := m.GetDutType().(*Dut_Android_); ok {
+		return x.Android
+	}
+	return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*Dut) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*Dut_Chromeos)(nil),
+		(*Dut_Android_)(nil),
+	}
+}
+
 // Unique identifier for the lab device.
 type Dut_Id struct {
 	Value                string   `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
@@ -111,28 +150,171 @@
 	return ""
 }
 
+// Chrome OS specific DUT details
+type Dut_ChromeOS struct {
+	// Manufacturing hardware design/component details for a given device
+	MfgConfig *api.MfgConfig `protobuf:"bytes,1,opt,name=mfg_config,json=mfgConfig,proto3" json:"mfg_config,omitempty"`
+	// Endpoint for ssh service running on the device
+	Ssh                  *IpEndpoint `protobuf:"bytes,2,opt,name=ssh,proto3" json:"ssh,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *Dut_ChromeOS) Reset()         { *m = Dut_ChromeOS{} }
+func (m *Dut_ChromeOS) String() string { return proto.CompactTextString(m) }
+func (*Dut_ChromeOS) ProtoMessage()    {}
+func (*Dut_ChromeOS) Descriptor() ([]byte, []int) {
+	return fileDescriptor_675cb56828cc6988, []int{0, 1}
+}
+
+func (m *Dut_ChromeOS) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Dut_ChromeOS.Unmarshal(m, b)
+}
+func (m *Dut_ChromeOS) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Dut_ChromeOS.Marshal(b, m, deterministic)
+}
+func (m *Dut_ChromeOS) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Dut_ChromeOS.Merge(m, src)
+}
+func (m *Dut_ChromeOS) XXX_Size() int {
+	return xxx_messageInfo_Dut_ChromeOS.Size(m)
+}
+func (m *Dut_ChromeOS) XXX_DiscardUnknown() {
+	xxx_messageInfo_Dut_ChromeOS.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Dut_ChromeOS proto.InternalMessageInfo
+
+func (m *Dut_ChromeOS) GetMfgConfig() *api.MfgConfig {
+	if m != nil {
+		return m.MfgConfig
+	}
+	return nil
+}
+
+func (m *Dut_ChromeOS) GetSsh() *IpEndpoint {
+	if m != nil {
+		return m.Ssh
+	}
+	return nil
+}
+
+// Android specific DUT details
+type Dut_Android struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Dut_Android) Reset()         { *m = Dut_Android{} }
+func (m *Dut_Android) String() string { return proto.CompactTextString(m) }
+func (*Dut_Android) ProtoMessage()    {}
+func (*Dut_Android) Descriptor() ([]byte, []int) {
+	return fileDescriptor_675cb56828cc6988, []int{0, 2}
+}
+
+func (m *Dut_Android) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Dut_Android.Unmarshal(m, b)
+}
+func (m *Dut_Android) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Dut_Android.Marshal(b, m, deterministic)
+}
+func (m *Dut_Android) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Dut_Android.Merge(m, src)
+}
+func (m *Dut_Android) XXX_Size() int {
+	return xxx_messageInfo_Dut_Android.Size(m)
+}
+func (m *Dut_Android) XXX_DiscardUnknown() {
+	xxx_messageInfo_Dut_Android.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Dut_Android proto.InternalMessageInfo
+
+// Defines the testing topology for a given device under test
+type DutTopology struct {
+	// Primary device under test in the topology
+	Dut *Dut `protobuf:"bytes,1,opt,name=dut,proto3" json:"dut,omitempty"`
+	// Secondary devices used for peer-to-peer testing.
+	PeerDuts             []*Dut   `protobuf:"bytes,2,rep,name=peer_duts,json=peerDuts,proto3" json:"peer_duts,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *DutTopology) Reset()         { *m = DutTopology{} }
+func (m *DutTopology) String() string { return proto.CompactTextString(m) }
+func (*DutTopology) ProtoMessage()    {}
+func (*DutTopology) Descriptor() ([]byte, []int) {
+	return fileDescriptor_675cb56828cc6988, []int{1}
+}
+
+func (m *DutTopology) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_DutTopology.Unmarshal(m, b)
+}
+func (m *DutTopology) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_DutTopology.Marshal(b, m, deterministic)
+}
+func (m *DutTopology) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DutTopology.Merge(m, src)
+}
+func (m *DutTopology) XXX_Size() int {
+	return xxx_messageInfo_DutTopology.Size(m)
+}
+func (m *DutTopology) XXX_DiscardUnknown() {
+	xxx_messageInfo_DutTopology.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DutTopology proto.InternalMessageInfo
+
+func (m *DutTopology) GetDut() *Dut {
+	if m != nil {
+		return m.Dut
+	}
+	return nil
+}
+
+func (m *DutTopology) GetPeerDuts() []*Dut {
+	if m != nil {
+		return m.PeerDuts
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*Dut)(nil), "chromiumos.test.lab.api.Dut")
 	proto.RegisterType((*Dut_Id)(nil), "chromiumos.test.lab.api.Dut.Id")
+	proto.RegisterType((*Dut_ChromeOS)(nil), "chromiumos.test.lab.api.Dut.ChromeOS")
+	proto.RegisterType((*Dut_Android)(nil), "chromiumos.test.lab.api.Dut.Android")
+	proto.RegisterType((*DutTopology)(nil), "chromiumos.test.lab.api.DutTopology")
 }
 
 func init() { proto.RegisterFile("chromiumos/test/lab/api/dut.proto", fileDescriptor_675cb56828cc6988) }
 
 var fileDescriptor_675cb56828cc6988 = []byte{
-	// 227 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0xce, 0x28, 0xca,
-	0xcf, 0xcd, 0x2c, 0xcd, 0xcd, 0x2f, 0xd6, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0xcf, 0x49, 0x4c, 0xd2,
-	0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0x29, 0x2d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x47,
-	0x28, 0xd1, 0x03, 0x29, 0xd1, 0xcb, 0x49, 0x4c, 0xd2, 0x4b, 0x2c, 0xc8, 0x94, 0x52, 0x43, 0xd2,
-	0x9b, 0x9c, 0x9f, 0x97, 0x96, 0x99, 0x0e, 0xd6, 0x99, 0x9b, 0x96, 0x1e, 0x0f, 0xe1, 0x42, 0x0c,
-	0x90, 0xd2, 0xc4, 0x65, 0x47, 0x66, 0x41, 0x7c, 0x6a, 0x5e, 0x4a, 0x41, 0x7e, 0x66, 0x1e, 0xd4,
-	0x2e, 0xa5, 0x99, 0x8c, 0x5c, 0xcc, 0x2e, 0xa5, 0x25, 0x42, 0xf6, 0x5c, 0x5c, 0x08, 0x63, 0x24,
-	0x98, 0x14, 0x18, 0x35, 0xb8, 0x8d, 0x14, 0xf4, 0x90, 0x1c, 0x02, 0xb5, 0x20, 0xb1, 0x20, 0x53,
-	0xcf, 0x37, 0x2d, 0xdd, 0x19, 0xcc, 0x0b, 0xe2, 0xcc, 0x85, 0x31, 0x85, 0x4c, 0xb9, 0x98, 0x8b,
-	0x8b, 0x33, 0x24, 0x98, 0xc1, 0x3a, 0x95, 0xf5, 0x70, 0x78, 0x41, 0xcf, 0xb3, 0xc0, 0x15, 0xea,
-	0x80, 0x20, 0x90, 0x7a, 0x29, 0x29, 0x2e, 0x26, 0xcf, 0x14, 0x21, 0x11, 0x2e, 0xd6, 0xb2, 0xc4,
-	0x9c, 0xd2, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x08, 0xc7, 0xc9, 0x38, 0xca, 0x30,
-	0x3d, 0x1f, 0x6e, 0x92, 0x5e, 0x7e, 0x51, 0xba, 0x3e, 0x66, 0x00, 0xa4, 0xe7, 0xa3, 0x78, 0x31,
-	0x89, 0x0d, 0xec, 0x2f, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xcd, 0x13, 0x25, 0x68,
-	0x01, 0x00, 0x00,
+	// 354 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcd, 0x4b, 0xc3, 0x40,
+	0x10, 0xc5, 0xdb, 0x04, 0x6d, 0x32, 0xbd, 0x2d, 0x82, 0x25, 0x08, 0xd6, 0xfa, 0x81, 0x5e, 0x36,
+	0x68, 0xf1, 0xe0, 0x49, 0x6d, 0x23, 0xb4, 0x07, 0x11, 0xa2, 0x27, 0x2f, 0x21, 0xed, 0xa6, 0xe9,
+	0x42, 0x92, 0x59, 0x92, 0x5d, 0xb1, 0x57, 0xff, 0x67, 0xef, 0x92, 0x4d, 0xfa, 0x21, 0xd2, 0x7a,
+	0xdb, 0x85, 0xdf, 0x9b, 0xf7, 0xe6, 0x31, 0x70, 0x32, 0x9d, 0xe7, 0x98, 0x72, 0x95, 0x62, 0xe1,
+	0xca, 0xa8, 0x90, 0x6e, 0x12, 0x4e, 0xdc, 0x50, 0x70, 0x97, 0x29, 0x49, 0x45, 0x8e, 0x12, 0xc9,
+	0xe1, 0x1a, 0xa1, 0x25, 0x42, 0x93, 0x70, 0x42, 0x43, 0xc1, 0x9d, 0x8b, 0x0d, 0xed, 0x14, 0xb3,
+	0x19, 0x8f, 0xb5, 0x32, 0x9d, 0xc5, 0x41, 0xf5, 0xad, 0x06, 0x38, 0x57, 0xdb, 0x3c, 0xb8, 0x08,
+	0xa2, 0x8c, 0x09, 0xe4, 0x59, 0xed, 0xd5, 0xfb, 0x36, 0xc0, 0xf4, 0x94, 0x24, 0x2e, 0x18, 0x9c,
+	0x75, 0x9a, 0xdd, 0xe6, 0x65, 0xfb, 0xe6, 0x98, 0x6e, 0x09, 0x40, 0x3d, 0x25, 0xe9, 0x98, 0xf9,
+	0x06, 0x67, 0x64, 0x08, 0x96, 0xa6, 0x22, 0x2c, 0x3a, 0x86, 0x96, 0x9d, 0xef, 0x94, 0x0d, 0x35,
+	0xfc, 0xf2, 0x3a, 0x6a, 0xf8, 0x2b, 0x21, 0x79, 0x80, 0x56, 0x98, 0xb1, 0x1c, 0x39, 0xeb, 0x98,
+	0x7a, 0xc6, 0xd9, 0xce, 0x19, 0x8f, 0x15, 0x3b, 0x6a, 0xf8, 0x4b, 0x99, 0xe3, 0x80, 0x31, 0x66,
+	0xe4, 0x00, 0xf6, 0x3e, 0xc2, 0x44, 0x45, 0x7a, 0x01, 0xdb, 0xaf, 0x3e, 0xce, 0x57, 0x13, 0xac,
+	0xa5, 0x2d, 0xb9, 0x07, 0x58, 0xf7, 0x54, 0x2f, 0xda, 0xdd, 0x74, 0xab, 0x1b, 0x2c, 0xbd, 0x9e,
+	0x67, 0xf1, 0x50, 0xff, 0x7c, 0x3b, 0x5d, 0x3e, 0xc9, 0x2d, 0x98, 0x45, 0x31, 0xaf, 0x77, 0x3d,
+	0xdd, 0x9a, 0x73, 0x2c, 0x9e, 0xea, 0x86, 0xfd, 0x92, 0x77, 0x6c, 0x68, 0xd5, 0xb1, 0x07, 0x00,
+	0x16, 0x53, 0x32, 0x90, 0x0b, 0x11, 0xf5, 0x3e, 0xa1, 0xed, 0x29, 0xf9, 0x86, 0x02, 0x13, 0x8c,
+	0x17, 0x84, 0x82, 0xc9, 0x94, 0xac, 0x63, 0x1d, 0xed, 0x2a, 0xc1, 0x2f, 0x41, 0x72, 0x07, 0xb6,
+	0x88, 0xa2, 0x3c, 0x60, 0x4a, 0x96, 0xf5, 0x9b, 0xff, 0xaa, 0xac, 0x12, 0xf7, 0x94, 0x2c, 0x06,
+	0xfd, 0xf7, 0xeb, 0x18, 0x57, 0x2c, 0xc5, 0x3c, 0x76, 0xff, 0x9e, 0x55, 0x8c, 0xbf, 0x0e, 0x67,
+	0xb2, 0xaf, 0xaf, 0xa5, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x4a, 0x40, 0x69, 0x4f, 0xbe, 0x02,
+	0x00, 0x00,
 }
diff --git a/proto/chromiumos/test/lab/api/dut.proto b/proto/chromiumos/test/lab/api/dut.proto
index a214535..929a490 100644
--- a/proto/chromiumos/test/lab/api/dut.proto
+++ b/proto/chromiumos/test/lab/api/dut.proto
@@ -18,9 +18,31 @@
     string value = 1;
   }
 
-  // Manufacturing hardware design/component details for a given device
-  chromiumos.config.api.MfgConfig mfg_config = 2;
+  Id id = 1;
 
-  // Endpoint for ssh service running on the device
-  IpEndpoint ssh = 3;
+  oneof dut_type {
+    ChromeOS chromeos = 2;
+    Android android = 3;
+  }
+
+  // Chrome OS specific DUT details
+  message ChromeOS {
+    // Manufacturing hardware design/component details for a given device
+    chromiumos.config.api.MfgConfig mfg_config = 1;
+    // Endpoint for ssh service running on the device
+    IpEndpoint ssh = 2;
+  }
+
+  // Android specific DUT details
+  message Android {
+  }
 }
+
+// Defines the testing topology for a given device under test
+message DutTopology {
+  // Primary device under test in the topology
+  Dut dut = 1;
+  // Secondary devices used for peer-to-peer testing.
+  repeated Dut peer_duts = 2;
+}
+
diff --git a/python/chromiumos/test/lab/api/dut_pb2.py b/python/chromiumos/test/lab/api/dut_pb2.py
index 87285a5..b8ac452 100644
--- a/python/chromiumos/test/lab/api/dut_pb2.py
+++ b/python/chromiumos/test/lab/api/dut_pb2.py
@@ -21,7 +21,7 @@
   package='chromiumos.test.lab.api',
   syntax='proto3',
   serialized_options=_b('Z1go.chromium.org/chromiumos/config/go/test/lab/api'),
-  serialized_pb=_b('\n!chromiumos/test/lab/api/dut.proto\x12\x17\x63hromiumos.test.lab.api\x1a&chromiumos/config/api/mfg_config.proto\x1a)chromiumos/test/lab/api/ip_endpoint.proto\"\x82\x01\n\x03\x44ut\x12\x34\n\nmfg_config\x18\x02 \x01(\x0b\x32 .chromiumos.config.api.MfgConfig\x12\x30\n\x03ssh\x18\x03 \x01(\x0b\x32#.chromiumos.test.lab.api.IpEndpoint\x1a\x13\n\x02Id\x12\r\n\x05value\x18\x01 \x01(\tB3Z1go.chromium.org/chromiumos/config/go/test/lab/apib\x06proto3')
+  serialized_pb=_b('\n!chromiumos/test/lab/api/dut.proto\x12\x17\x63hromiumos.test.lab.api\x1a&chromiumos/config/api/mfg_config.proto\x1a)chromiumos/test/lab/api/ip_endpoint.proto\"\xc6\x02\n\x03\x44ut\x12+\n\x02id\x18\x01 \x01(\x0b\x32\x1f.chromiumos.test.lab.api.Dut.Id\x12\x39\n\x08\x63hromeos\x18\x02 \x01(\x0b\x32%.chromiumos.test.lab.api.Dut.ChromeOSH\x00\x12\x37\n\x07\x61ndroid\x18\x03 \x01(\x0b\x32$.chromiumos.test.lab.api.Dut.AndroidH\x00\x1a\x13\n\x02Id\x12\r\n\x05value\x18\x01 \x01(\t\x1ar\n\x08\x43hromeOS\x12\x34\n\nmfg_config\x18\x01 \x01(\x0b\x32 .chromiumos.config.api.MfgConfig\x12\x30\n\x03ssh\x18\x02 \x01(\x0b\x32#.chromiumos.test.lab.api.IpEndpoint\x1a\t\n\x07\x41ndroidB\n\n\x08\x64ut_type\"i\n\x0b\x44utTopology\x12)\n\x03\x64ut\x18\x01 \x01(\x0b\x32\x1c.chromiumos.test.lab.api.Dut\x12/\n\tpeer_duts\x18\x02 \x03(\x0b\x32\x1c.chromiumos.test.lab.api.DutB3Z1go.chromium.org/chromiumos/config/go/test/lab/apib\x06proto3')
   ,
   dependencies=[chromiumos_dot_config_dot_api_dot_mfg__config__pb2.DESCRIPTOR,chromiumos_dot_test_dot_lab_dot_api_dot_ip__endpoint__pb2.DESCRIPTOR,])
 
@@ -54,8 +54,68 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=257,
-  serialized_end=276,
+  serialized_start=314,
+  serialized_end=333,
+)
+
+_DUT_CHROMEOS = _descriptor.Descriptor(
+  name='ChromeOS',
+  full_name='chromiumos.test.lab.api.Dut.ChromeOS',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='mfg_config', full_name='chromiumos.test.lab.api.Dut.ChromeOS.mfg_config', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='ssh', full_name='chromiumos.test.lab.api.Dut.ChromeOS.ssh', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=335,
+  serialized_end=449,
+)
+
+_DUT_ANDROID = _descriptor.Descriptor(
+  name='Android',
+  full_name='chromiumos.test.lab.api.Dut.Android',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=451,
+  serialized_end=460,
 )
 
 _DUT = _descriptor.Descriptor(
@@ -66,14 +126,21 @@
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='mfg_config', full_name='chromiumos.test.lab.api.Dut.mfg_config', index=0,
+      name='id', full_name='chromiumos.test.lab.api.Dut.id', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='chromeos', full_name='chromiumos.test.lab.api.Dut.chromeos', index=1,
       number=2, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='ssh', full_name='chromiumos.test.lab.api.Dut.ssh', index=1,
+      name='android', full_name='chromiumos.test.lab.api.Dut.android', index=2,
       number=3, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
@@ -82,7 +149,48 @@
   ],
   extensions=[
   ],
-  nested_types=[_DUT_ID, ],
+  nested_types=[_DUT_ID, _DUT_CHROMEOS, _DUT_ANDROID, ],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+    _descriptor.OneofDescriptor(
+      name='dut_type', full_name='chromiumos.test.lab.api.Dut.dut_type',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=146,
+  serialized_end=472,
+)
+
+
+_DUTTOPOLOGY = _descriptor.Descriptor(
+  name='DutTopology',
+  full_name='chromiumos.test.lab.api.DutTopology',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='dut', full_name='chromiumos.test.lab.api.DutTopology.dut', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='peer_duts', full_name='chromiumos.test.lab.api.DutTopology.peer_duts', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
   enum_types=[
   ],
   serialized_options=None,
@@ -91,14 +199,28 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=146,
-  serialized_end=276,
+  serialized_start=474,
+  serialized_end=579,
 )
 
 _DUT_ID.containing_type = _DUT
-_DUT.fields_by_name['mfg_config'].message_type = chromiumos_dot_config_dot_api_dot_mfg__config__pb2._MFGCONFIG
-_DUT.fields_by_name['ssh'].message_type = chromiumos_dot_test_dot_lab_dot_api_dot_ip__endpoint__pb2._IPENDPOINT
+_DUT_CHROMEOS.fields_by_name['mfg_config'].message_type = chromiumos_dot_config_dot_api_dot_mfg__config__pb2._MFGCONFIG
+_DUT_CHROMEOS.fields_by_name['ssh'].message_type = chromiumos_dot_test_dot_lab_dot_api_dot_ip__endpoint__pb2._IPENDPOINT
+_DUT_CHROMEOS.containing_type = _DUT
+_DUT_ANDROID.containing_type = _DUT
+_DUT.fields_by_name['id'].message_type = _DUT_ID
+_DUT.fields_by_name['chromeos'].message_type = _DUT_CHROMEOS
+_DUT.fields_by_name['android'].message_type = _DUT_ANDROID
+_DUT.oneofs_by_name['dut_type'].fields.append(
+  _DUT.fields_by_name['chromeos'])
+_DUT.fields_by_name['chromeos'].containing_oneof = _DUT.oneofs_by_name['dut_type']
+_DUT.oneofs_by_name['dut_type'].fields.append(
+  _DUT.fields_by_name['android'])
+_DUT.fields_by_name['android'].containing_oneof = _DUT.oneofs_by_name['dut_type']
+_DUTTOPOLOGY.fields_by_name['dut'].message_type = _DUT
+_DUTTOPOLOGY.fields_by_name['peer_duts'].message_type = _DUT
 DESCRIPTOR.message_types_by_name['Dut'] = _DUT
+DESCRIPTOR.message_types_by_name['DutTopology'] = _DUTTOPOLOGY
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
 Dut = _reflection.GeneratedProtocolMessageType('Dut', (_message.Message,), dict(
@@ -109,12 +231,35 @@
     # @@protoc_insertion_point(class_scope:chromiumos.test.lab.api.Dut.Id)
     ))
   ,
+
+  ChromeOS = _reflection.GeneratedProtocolMessageType('ChromeOS', (_message.Message,), dict(
+    DESCRIPTOR = _DUT_CHROMEOS,
+    __module__ = 'chromiumos.test.lab.api.dut_pb2'
+    # @@protoc_insertion_point(class_scope:chromiumos.test.lab.api.Dut.ChromeOS)
+    ))
+  ,
+
+  Android = _reflection.GeneratedProtocolMessageType('Android', (_message.Message,), dict(
+    DESCRIPTOR = _DUT_ANDROID,
+    __module__ = 'chromiumos.test.lab.api.dut_pb2'
+    # @@protoc_insertion_point(class_scope:chromiumos.test.lab.api.Dut.Android)
+    ))
+  ,
   DESCRIPTOR = _DUT,
   __module__ = 'chromiumos.test.lab.api.dut_pb2'
   # @@protoc_insertion_point(class_scope:chromiumos.test.lab.api.Dut)
   ))
 _sym_db.RegisterMessage(Dut)
 _sym_db.RegisterMessage(Dut.Id)
+_sym_db.RegisterMessage(Dut.ChromeOS)
+_sym_db.RegisterMessage(Dut.Android)
+
+DutTopology = _reflection.GeneratedProtocolMessageType('DutTopology', (_message.Message,), dict(
+  DESCRIPTOR = _DUTTOPOLOGY,
+  __module__ = 'chromiumos.test.lab.api.dut_pb2'
+  # @@protoc_insertion_point(class_scope:chromiumos.test.lab.api.DutTopology)
+  ))
+_sym_db.RegisterMessage(DutTopology)
 
 
 DESCRIPTOR._options = None