diff --git a/docs/slurm.md b/docs/slurm.md index cc365b1..db671bd 100644 --- a/docs/slurm.md +++ b/docs/slurm.md @@ -47,7 +47,7 @@ Then run the topograph service as normal. You must then start the toposim service as such, setting the path to the test model that you want to use in simulation: ```bash -/usr/local/bin/topograph -m /usr/local/bin/tests/models/.yaml +/usr/local/bin/toposim -m /usr/local/bin/tests/models/.yaml ``` You can then verify the topology results via simulation by querying topograph, and specifying the test model path as a parameter to the provider. diff --git a/pkg/engines/k8s/labeler_test.go b/pkg/engines/k8s/labeler_test.go index ceb563f..a911718 100644 --- a/pkg/engines/k8s/labeler_test.go +++ b/pkg/engines/k8s/labeler_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/NVIDIA/topograph/pkg/topology" "github.com/NVIDIA/topograph/pkg/translate" ) @@ -40,6 +41,7 @@ func (l *testLabeler) AddNodeLabels(_ context.Context, nodeName string, labels m func TestApplyNodeLabels(t *testing.T) { root, _ := translate.GetTreeTestSet(true) + require.Equal(t, root.Metadata[topology.KeyPlugin], topology.TopologyTree) labeler := &testLabeler{data: make(map[string]map[string]string)} data := map[string]map[string]string{ "Node201": {"topology.kubernetes.io/network-level-1": "S2", "topology.kubernetes.io/network-level-2": "S1"}, diff --git a/pkg/engines/slurm/slurm.go b/pkg/engines/slurm/slurm.go index 3fed155..fcd6e68 100644 --- a/pkg/engines/slurm/slurm.go +++ b/pkg/engines/slurm/slurm.go @@ -148,7 +148,7 @@ func GenerateOutputParams(ctx context.Context, tree *topology.Vertex, params *Pa plugin = tree.Metadata[topology.KeyPlugin] } if len(plugin) == 0 { - plugin = topology.ValTopologyTree + plugin = topology.TopologyTree } if _, err := buf.WriteString(fmt.Sprintf(TopologyHeader, plugin)); err != nil { return nil, err diff --git a/pkg/engines/test/test.go b/pkg/engines/test/test.go index 5bdcc0e..40aef60 100644 --- a/pkg/engines/test/test.go +++ b/pkg/engines/test/test.go @@ -19,6 +19,7 @@ package test import ( "context" "errors" + "fmt" "github.com/NVIDIA/topograph/internal/config" "github.com/NVIDIA/topograph/pkg/engines" @@ -59,10 +60,14 @@ func (eng *TestEngine) GenerateOutput(ctx context.Context, tree *topology.Vertex } if len(tree.Metadata) == 0 { - tree.Metadata = make(map[string]string) + return nil, fmt.Errorf("metadata for test engine not set") } - tree.Metadata[topology.KeyPlugin] = p.Plugin - tree.Metadata[topology.KeyBlockSizes] = p.BlockSizes + if len(p.Plugin) != 0 { + tree.Metadata[topology.KeyPlugin] = p.Plugin + } + if len(p.BlockSizes) != 0 { + tree.Metadata[topology.KeyBlockSizes] = p.BlockSizes + } return slurm.GenerateOutputParams(ctx, tree, &p) } diff --git a/pkg/ib/ib.go b/pkg/ib/ib.go index d0a48b4..dc038e7 100644 --- a/pkg/ib/ib.go +++ b/pkg/ib/ib.go @@ -64,7 +64,14 @@ func GenerateTopologyConfig(data []byte) (*topology.Vertex, error) { } seen = make(map[int]map[string]*Switch) root.simplify(root.getHeight()) - return root.toGraph() + rootNode, err := root.toGraph() + if err != nil { + return nil, err + } + rootNode.Metadata = map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + } + return rootNode, nil } func (sw *Switch) toGraph() (*topology.Vertex, error) { diff --git a/pkg/models/model.go b/pkg/models/model.go index 1d61743..4aac239 100644 --- a/pkg/models/model.go +++ b/pkg/models/model.go @@ -140,12 +140,13 @@ func getNetworkLayers(name string, swmap map[string]string) ([]string, error) { } } -func (model *Model) ToTree() (*topology.Vertex, map[string]string) { +func (model *Model) ToGraph() (*topology.Vertex, map[string]string) { instance2node := make(map[string]string) nodeVertexMap := make(map[string]*topology.Vertex) swVertexMap := make(map[string]*topology.Vertex) swRootMap := make(map[string]bool) blockVertexMap := make(map[string]*topology.Vertex) + var block_topology bool = false // Create all the vertices for each node for k, v := range model.Nodes { @@ -165,6 +166,9 @@ func (model *Model) ToTree() (*topology.Vertex, map[string]string) { for _, node := range cb.Nodes { blockVertexMap[cb.Name].Vertices[node] = nodeVertexMap[node] } + if len(cb.NVLink) != 0 { + block_topology = true + } } // Connect all the switches to their sub-switches and sub-nodes @@ -196,11 +200,13 @@ func (model *Model) ToTree() (*topology.Vertex, map[string]string) { for k, v := range blockVertexMap { blockRoot.Vertices[k] = v } - root := &topology.Vertex{ - Vertices: map[string]*topology.Vertex{ - topology.ValTopologyBlock: blockRoot, - topology.ValTopologyTree: treeRoot, - }, + if block_topology { + root := &topology.Vertex{ + Vertices: map[string]*topology.Vertex{topology.TopologyBlock: blockRoot, topology.TopologyTree: treeRoot}, + Metadata: map[string]string{topology.KeyPlugin: topology.TopologyBlock}, + } + return root, instance2node } - return root, instance2node + treeRoot.Metadata = map[string]string{topology.KeyPlugin: topology.TopologyTree} + return treeRoot, instance2node } diff --git a/pkg/protos/topology.pb.go b/pkg/protos/topology.pb.go index 3b950c7..3d49c10 100644 --- a/pkg/protos/topology.pb.go +++ b/pkg/protos/topology.pb.go @@ -1,329 +1,236 @@ -// -// Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. -// -// 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. - // Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.35.1 -// protoc v5.28.2 // source: topology.proto package protos import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" ) -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf -type TopologyRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"` - InstanceIds []string `protobuf:"bytes,3,rep,name=instance_ids,json=instanceIds,proto3" json:"instance_ids,omitempty"` +type TopologyRequest struct { + Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"` + InstanceIds []string `protobuf:"bytes,3,rep,name=instance_ids,json=instanceIds,proto3" json:"instance_ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TopologyRequest) Reset() { - *x = TopologyRequest{} - mi := &file_topology_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (m *TopologyRequest) Reset() { *m = TopologyRequest{} } +func (m *TopologyRequest) String() string { return proto.CompactTextString(m) } +func (*TopologyRequest) ProtoMessage() {} +func (*TopologyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a326f5bb56fea2fc, []int{0} } -func (x *TopologyRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TopologyRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TopologyRequest.Unmarshal(m, b) } - -func (*TopologyRequest) ProtoMessage() {} - -func (x *TopologyRequest) ProtoReflect() protoreflect.Message { - mi := &file_topology_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TopologyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TopologyRequest.Marshal(b, m, deterministic) } - -// Deprecated: Use TopologyRequest.ProtoReflect.Descriptor instead. -func (*TopologyRequest) Descriptor() ([]byte, []int) { - return file_topology_proto_rawDescGZIP(), []int{0} +func (m *TopologyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TopologyRequest.Merge(m, src) +} +func (m *TopologyRequest) XXX_Size() int { + return xxx_messageInfo_TopologyRequest.Size(m) } +func (m *TopologyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TopologyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TopologyRequest proto.InternalMessageInfo -func (x *TopologyRequest) GetProvider() string { - if x != nil { - return x.Provider +func (m *TopologyRequest) GetProvider() string { + if m != nil { + return m.Provider } return "" } -func (x *TopologyRequest) GetRegion() string { - if x != nil { - return x.Region +func (m *TopologyRequest) GetRegion() string { + if m != nil { + return m.Region } return "" } -func (x *TopologyRequest) GetInstanceIds() []string { - if x != nil { - return x.InstanceIds +func (m *TopologyRequest) GetInstanceIds() []string { + if m != nil { + return m.InstanceIds } return nil } type TopologyResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Instances []*Instance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` + Instances []*Instance `protobuf:"bytes,1,rep,name=instances,proto3" json:"instances,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TopologyResponse) Reset() { - *x = TopologyResponse{} - mi := &file_topology_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (m *TopologyResponse) Reset() { *m = TopologyResponse{} } +func (m *TopologyResponse) String() string { return proto.CompactTextString(m) } +func (*TopologyResponse) ProtoMessage() {} +func (*TopologyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a326f5bb56fea2fc, []int{1} } -func (x *TopologyResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *TopologyResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TopologyResponse.Unmarshal(m, b) } - -func (*TopologyResponse) ProtoMessage() {} - -func (x *TopologyResponse) ProtoReflect() protoreflect.Message { - mi := &file_topology_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *TopologyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TopologyResponse.Marshal(b, m, deterministic) } - -// Deprecated: Use TopologyResponse.ProtoReflect.Descriptor instead. -func (*TopologyResponse) Descriptor() ([]byte, []int) { - return file_topology_proto_rawDescGZIP(), []int{1} +func (m *TopologyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TopologyResponse.Merge(m, src) +} +func (m *TopologyResponse) XXX_Size() int { + return xxx_messageInfo_TopologyResponse.Size(m) +} +func (m *TopologyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TopologyResponse.DiscardUnknown(m) } -func (x *TopologyResponse) GetInstances() []*Instance { - if x != nil { - return x.Instances +var xxx_messageInfo_TopologyResponse proto.InternalMessageInfo + +func (m *TopologyResponse) GetInstances() []*Instance { + if m != nil { + return m.Instances } return nil } type Instance struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - InstanceType string `protobuf:"bytes,2,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` - Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` - Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"` - DataCenter string `protobuf:"bytes,5,opt,name=data_center,json=dataCenter,proto3" json:"data_center,omitempty"` - NetworkLayers []string `protobuf:"bytes,6,rep,name=network_layers,json=networkLayers,proto3" json:"network_layers,omitempty"` - NvlinkDomain string `protobuf:"bytes,7,opt,name=nvlink_domain,json=nvlinkDomain,proto3" json:"nvlink_domain,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + InstanceType string `protobuf:"bytes,2,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` + Provider string `protobuf:"bytes,3,opt,name=provider,proto3" json:"provider,omitempty"` + Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"` + DataCenter string `protobuf:"bytes,5,opt,name=data_center,json=dataCenter,proto3" json:"data_center,omitempty"` + NetworkLayers []string `protobuf:"bytes,6,rep,name=network_layers,json=networkLayers,proto3" json:"network_layers,omitempty"` + NvlinkDomain string `protobuf:"bytes,7,opt,name=nvlink_domain,json=nvlinkDomain,proto3" json:"nvlink_domain,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Instance) Reset() { *m = Instance{} } +func (m *Instance) String() string { return proto.CompactTextString(m) } +func (*Instance) ProtoMessage() {} +func (*Instance) Descriptor() ([]byte, []int) { + return fileDescriptor_a326f5bb56fea2fc, []int{2} } -func (x *Instance) Reset() { - *x = Instance{} - mi := &file_topology_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (m *Instance) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Instance.Unmarshal(m, b) } - -func (x *Instance) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *Instance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Instance.Marshal(b, m, deterministic) } - -func (*Instance) ProtoMessage() {} - -func (x *Instance) ProtoReflect() protoreflect.Message { - mi := &file_topology_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) +func (m *Instance) XXX_Merge(src proto.Message) { + xxx_messageInfo_Instance.Merge(m, src) } - -// Deprecated: Use Instance.ProtoReflect.Descriptor instead. -func (*Instance) Descriptor() ([]byte, []int) { - return file_topology_proto_rawDescGZIP(), []int{2} +func (m *Instance) XXX_Size() int { + return xxx_messageInfo_Instance.Size(m) } +func (m *Instance) XXX_DiscardUnknown() { + xxx_messageInfo_Instance.DiscardUnknown(m) +} + +var xxx_messageInfo_Instance proto.InternalMessageInfo -func (x *Instance) GetId() string { - if x != nil { - return x.Id +func (m *Instance) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *Instance) GetInstanceType() string { - if x != nil { - return x.InstanceType +func (m *Instance) GetInstanceType() string { + if m != nil { + return m.InstanceType } return "" } -func (x *Instance) GetProvider() string { - if x != nil { - return x.Provider +func (m *Instance) GetProvider() string { + if m != nil { + return m.Provider } return "" } -func (x *Instance) GetRegion() string { - if x != nil { - return x.Region +func (m *Instance) GetRegion() string { + if m != nil { + return m.Region } return "" } -func (x *Instance) GetDataCenter() string { - if x != nil { - return x.DataCenter +func (m *Instance) GetDataCenter() string { + if m != nil { + return m.DataCenter } return "" } -func (x *Instance) GetNetworkLayers() []string { - if x != nil { - return x.NetworkLayers +func (m *Instance) GetNetworkLayers() []string { + if m != nil { + return m.NetworkLayers } return nil } -func (x *Instance) GetNvlinkDomain() string { - if x != nil { - return x.NvlinkDomain +func (m *Instance) GetNvlinkDomain() string { + if m != nil { + return m.NvlinkDomain } return "" } -var File_topology_proto protoreflect.FileDescriptor - -var file_topology_proto_rawDesc = []byte{ - 0x0a, 0x0e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x22, 0x68, 0x0a, 0x0f, 0x54, 0x6f, - 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, - 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x49, 0x64, 0x73, 0x22, 0x44, 0x0a, 0x10, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, - 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xe0, 0x01, 0x0a, 0x08, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, - 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, - 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, - 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x76, 0x6c, 0x69, - 0x6e, 0x6b, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x6e, 0x76, 0x6c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x32, 0x5e, 0x0a, - 0x0f, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x4b, 0x0a, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x6f, 0x70, 0x6f, - 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x19, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, - 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0b, 0x5a, - 0x09, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, -} - -var ( - file_topology_proto_rawDescOnce sync.Once - file_topology_proto_rawDescData = file_topology_proto_rawDesc -) - -func file_topology_proto_rawDescGZIP() []byte { - file_topology_proto_rawDescOnce.Do(func() { - file_topology_proto_rawDescData = protoimpl.X.CompressGZIP(file_topology_proto_rawDescData) - }) - return file_topology_proto_rawDescData -} - -var file_topology_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_topology_proto_goTypes = []any{ - (*TopologyRequest)(nil), // 0: topology.TopologyRequest - (*TopologyResponse)(nil), // 1: topology.TopologyResponse - (*Instance)(nil), // 2: topology.Instance -} -var file_topology_proto_depIdxs = []int32{ - 2, // 0: topology.TopologyResponse.instances:type_name -> topology.Instance - 0, // 1: topology.TopologyService.DescribeTopology:input_type -> topology.TopologyRequest - 1, // 2: topology.TopologyService.DescribeTopology:output_type -> topology.TopologyResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_topology_proto_init() } -func file_topology_proto_init() { - if File_topology_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_topology_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_topology_proto_goTypes, - DependencyIndexes: file_topology_proto_depIdxs, - MessageInfos: file_topology_proto_msgTypes, - }.Build() - File_topology_proto = out.File - file_topology_proto_rawDesc = nil - file_topology_proto_goTypes = nil - file_topology_proto_depIdxs = nil +func init() { + proto.RegisterType((*TopologyRequest)(nil), "topology.TopologyRequest") + proto.RegisterType((*TopologyResponse)(nil), "topology.TopologyResponse") + proto.RegisterType((*Instance)(nil), "topology.Instance") +} + +func init() { + proto.RegisterFile("topology.proto", fileDescriptor_a326f5bb56fea2fc) +} + +var fileDescriptor_a326f5bb56fea2fc = []byte{ + // 319 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x4f, 0xf2, 0x40, + 0x10, 0x86, 0xbf, 0xd2, 0x4f, 0xa4, 0x53, 0x41, 0xb2, 0x07, 0xb3, 0x72, 0x11, 0x6b, 0x4c, 0x38, + 0xa1, 0xc1, 0xa3, 0x37, 0xe5, 0x42, 0xf4, 0x54, 0x39, 0x79, 0xb0, 0x29, 0xdd, 0x09, 0x6e, 0xc0, + 0xdd, 0x75, 0x77, 0xc5, 0xf4, 0xdf, 0xfa, 0x53, 0x0c, 0xcb, 0xb6, 0x0d, 0x46, 0x4f, 0xed, 0x3c, + 0xef, 0x24, 0x7d, 0x66, 0xa6, 0xd0, 0xb3, 0x52, 0xc9, 0xb5, 0x5c, 0x96, 0x63, 0xa5, 0xa5, 0x95, + 0xa4, 0x53, 0xd5, 0xc9, 0x2b, 0x1c, 0xcf, 0xfd, 0x7b, 0x8a, 0xef, 0x1f, 0x68, 0x2c, 0x19, 0x40, + 0x47, 0x69, 0xb9, 0xe1, 0x0c, 0x35, 0x0d, 0x86, 0xc1, 0x28, 0x4a, 0xeb, 0x9a, 0x9c, 0x40, 0x5b, + 0xe3, 0x92, 0x4b, 0x41, 0x5b, 0x2e, 0xf1, 0x15, 0x39, 0x87, 0x23, 0x2e, 0x8c, 0xcd, 0x45, 0x81, + 0x19, 0x67, 0x86, 0x86, 0xc3, 0x70, 0x14, 0xa5, 0x71, 0xc5, 0x66, 0xcc, 0x24, 0x53, 0xe8, 0x37, + 0x5f, 0x32, 0x4a, 0x0a, 0x83, 0xe4, 0x1a, 0xa2, 0xaa, 0xc5, 0xd0, 0x60, 0x18, 0x8e, 0xe2, 0x09, + 0x19, 0xd7, 0xae, 0x33, 0x1f, 0xa5, 0x4d, 0x53, 0xf2, 0x15, 0x40, 0xa7, 0xe2, 0xa4, 0x07, 0x2d, + 0xce, 0xbc, 0x63, 0x8b, 0x33, 0x72, 0x01, 0xdd, 0xda, 0xc2, 0x96, 0x0a, 0xbd, 0x64, 0xad, 0x36, + 0x2f, 0x15, 0xee, 0x8d, 0x17, 0xfe, 0x39, 0xde, 0xff, 0xbd, 0xf1, 0xce, 0x20, 0x66, 0xb9, 0xcd, + 0xb3, 0x02, 0x85, 0x45, 0x4d, 0x0f, 0x5c, 0x08, 0x5b, 0x74, 0xef, 0x08, 0xb9, 0x84, 0x9e, 0x40, + 0xfb, 0x29, 0xf5, 0x2a, 0x5b, 0xe7, 0x25, 0x6a, 0x43, 0xdb, 0x6e, 0x03, 0x5d, 0x4f, 0x1f, 0x1d, + 0xdc, 0x0a, 0x8a, 0xcd, 0x9a, 0x8b, 0x55, 0xc6, 0xe4, 0x5b, 0xce, 0x05, 0x3d, 0xdc, 0x09, 0xee, + 0xe0, 0xd4, 0xb1, 0xc9, 0x4b, 0x73, 0x92, 0x27, 0xd4, 0x1b, 0x5e, 0x20, 0x79, 0x80, 0xfe, 0x14, + 0x4d, 0xa1, 0xf9, 0x02, 0xab, 0x88, 0x9c, 0x36, 0x8b, 0xfa, 0x71, 0xc1, 0xc1, 0xe0, 0xb7, 0x68, + 0xb7, 0xf2, 0xe4, 0xdf, 0x5d, 0xfc, 0x1c, 0x8d, 0xaf, 0x6e, 0xdd, 0x8f, 0x60, 0x16, 0x6d, 0xf7, + 0xbc, 0xf9, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xc6, 0xe8, 0x6b, 0x22, 0x02, 0x00, 0x00, } diff --git a/pkg/protos/topology_grpc.pb.go b/pkg/protos/topology_grpc.pb.go index 9210432..a5ec5ea 100644 --- a/pkg/protos/topology_grpc.pb.go +++ b/pkg/protos/topology_grpc.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.28.2 +// - protoc v3.12.4 // source: topology.proto package protos diff --git a/pkg/providers/aws/instance_topology.go b/pkg/providers/aws/instance_topology.go index d7a478c..d0a0a0a 100644 --- a/pkg/providers/aws/instance_topology.go +++ b/pkg/providers/aws/instance_topology.go @@ -181,6 +181,9 @@ func toGraph(top []types.InstanceTopology, cis []topology.ComputeInstances) (*to root := &topology.Vertex{ Vertices: make(map[string]*topology.Vertex), + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, } for name, node := range forest { root.Vertices[name] = node diff --git a/pkg/providers/aws/instance_topology_test.go b/pkg/providers/aws/instance_topology_test.go index 036e43c..16c56e5 100644 --- a/pkg/providers/aws/instance_topology_test.go +++ b/pkg/providers/aws/instance_topology_test.go @@ -101,9 +101,17 @@ func TestNewInstanceTopology(t *testing.T) { }, } - v1 := &topology.Vertex{ID: "nn-098f9e7674016cb1c", Vertices: map[string]*topology.Vertex{"nn-224a2a4d9df61a975": v2}} + v1 := &topology.Vertex{ + ID: "nn-098f9e7674016cb1c", + Vertices: map[string]*topology.Vertex{"nn-224a2a4d9df61a975": v2}, + } - expected := &topology.Vertex{Vertices: map[string]*topology.Vertex{"nn-098f9e7674016cb1c": v1}} + expected := &topology.Vertex{ + Vertices: map[string]*topology.Vertex{"nn-098f9e7674016cb1c": v1}, + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, + } tree, err := toGraph(top, []topology.ComputeInstances{{Instances: i2n}}) require.NoError(t, err) diff --git a/pkg/providers/baremetal/mnnvl.go b/pkg/providers/baremetal/mnnvl.go index c5b9fc7..5808b73 100644 --- a/pkg/providers/baremetal/mnnvl.go +++ b/pkg/providers/baremetal/mnnvl.go @@ -197,7 +197,7 @@ func toGraph(domainMap map[string]domain, treeRoot *topology.Vertex) *topology.V blockRoot := &topology.Vertex{ Vertices: make(map[string]*topology.Vertex), } - root.Vertices[topology.ValTopologyTree] = treeRoot + root.Vertices[topology.TopologyTree] = treeRoot for domainName, domain := range domainMap { tree := &topology.Vertex{ ID: domainName, @@ -209,10 +209,9 @@ func toGraph(domainMap map[string]domain, treeRoot *topology.Vertex) *topology.V blockRoot.Vertices[domainName] = tree } // add root metadata - root.Metadata[topology.KeyEngine] = engines.EngineSLURM // TODO: Check if this should be dynamic - root.Metadata[topology.KeyPlugin] = topology.ValTopologyBlock - root.Vertices[topology.ValTopologyBlock] = blockRoot - + root.Metadata[topology.KeyEngine] = engines.EngineSLURM + root.Metadata[topology.KeyPlugin] = topology.TopologyBlock + root.Vertices[topology.TopologyBlock] = blockRoot return root } diff --git a/pkg/providers/gcp/instance_topology.go b/pkg/providers/gcp/instance_topology.go index 42561e1..f42b784 100644 --- a/pkg/providers/gcp/instance_topology.go +++ b/pkg/providers/gcp/instance_topology.go @@ -143,6 +143,9 @@ func (cfg *InstanceTopology) toGraph() (*topology.Vertex, error) { root := &topology.Vertex{ Vertices: make(map[string]*topology.Vertex), + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, } for name, node := range forest { root.Vertices[name] = node diff --git a/pkg/providers/oci/instance_topology.go b/pkg/providers/oci/instance_topology.go index 62fcf97..a35cfb1 100644 --- a/pkg/providers/oci/instance_topology.go +++ b/pkg/providers/oci/instance_topology.go @@ -222,6 +222,9 @@ func toGraph(bareMetalHostSummaries []*core.ComputeBareMetalHostSummary, cis []t root := &topology.Vertex{ Vertices: make(map[string]*topology.Vertex), + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, } for name, node := range forest { root.Vertices[name] = node diff --git a/pkg/providers/test/test.go b/pkg/providers/test/test.go index 0c2b769..94589b0 100644 --- a/pkg/providers/test/test.go +++ b/pkg/providers/test/test.go @@ -62,7 +62,7 @@ func New(cfg providers.Config) (*Provider, error) { if err != nil { return nil, err // Wrapped by models.NewModelFromFile } - provider.tree, provider.instance2node = model.ToTree() + provider.tree, provider.instance2node = model.ToGraph() } return provider, nil } diff --git a/pkg/server/grpc_client.go b/pkg/server/grpc_client.go index 7f1fb63..e2bcd53 100644 --- a/pkg/server/grpc_client.go +++ b/pkg/server/grpc_client.go @@ -71,7 +71,7 @@ func getTopologyFormat(params map[string]any) string { } } } - return topology.ValTopologyTree + return topology.TopologyTree } func toGraph(response *pb.TopologyResponse, cis []topology.ComputeInstances, format string) *topology.Vertex { @@ -171,7 +171,7 @@ func toGraph(response *pb.TopologyResponse, cis []topology.ComputeInstances, for } metadata := map[string]string{topology.KeyPlugin: format} - if format == topology.ValTopologyBlock { + if format == topology.TopologyBlock { blockRoot := &topology.Vertex{ Vertices: make(map[string]*topology.Vertex), } @@ -181,14 +181,13 @@ func toGraph(response *pb.TopologyResponse, cis []topology.ComputeInstances, for return &topology.Vertex{ Vertices: map[string]*topology.Vertex{ - topology.ValTopologyBlock: blockRoot, - topology.ValTopologyTree: treeRoot, + topology.TopologyBlock: blockRoot, + topology.TopologyTree: treeRoot, }, Metadata: metadata, } } else { treeRoot.Metadata = metadata - return treeRoot } } diff --git a/pkg/server/grpc_client_test.go b/pkg/server/grpc_client_test.go index 56b9007..5ea8301 100644 --- a/pkg/server/grpc_client_test.go +++ b/pkg/server/grpc_client_test.go @@ -139,11 +139,11 @@ func TestToGraph(t *testing.T) { treeRoot := &topology.Vertex{Vertices: map[string]*topology.Vertex{"nvlink-nv1": nv1, "sw3": sw3, topology.NoTopology: extra}} blockRoot := &topology.Vertex{Vertices: map[string]*topology.Vertex{"nvlink-nv1": nv1}} root := &topology.Vertex{ - Vertices: map[string]*topology.Vertex{topology.ValTopologyBlock: blockRoot, topology.ValTopologyTree: treeRoot}, - Metadata: map[string]string{topology.KeyPlugin: topology.ValTopologyBlock}, + Vertices: map[string]*topology.Vertex{topology.TopologyBlock: blockRoot, topology.TopologyTree: treeRoot}, + Metadata: map[string]string{topology.KeyPlugin: topology.TopologyBlock}, } - require.Equal(t, root, toGraph(&pb.TopologyResponse{Instances: instances}, cis, topology.ValTopologyBlock)) + require.Equal(t, root, toGraph(&pb.TopologyResponse{Instances: instances}, cis, topology.TopologyBlock)) } func TestGetTopologyFormat(t *testing.T) { @@ -155,27 +155,27 @@ func TestGetTopologyFormat(t *testing.T) { { name: "Case 1: nil params", params: nil, - format: topology.ValTopologyTree, + format: topology.TopologyTree, }, { name: "Case 2: empty params", params: make(map[string]any), - format: topology.ValTopologyTree, + format: topology.TopologyTree, }, { name: "Case 3: missing key", params: map[string]any{"a": "b"}, - format: topology.ValTopologyTree, + format: topology.TopologyTree, }, { name: "Case 4: block topology", - params: map[string]any{topology.KeyPlugin: topology.ValTopologyBlock}, - format: topology.ValTopologyBlock, + params: map[string]any{topology.KeyPlugin: topology.TopologyBlock}, + format: topology.TopologyBlock, }, { name: "Case 5: tree topology", - params: map[string]any{topology.KeyPlugin: topology.ValTopologyTree}, - format: topology.ValTopologyTree, + params: map[string]any{topology.KeyPlugin: topology.TopologyTree}, + format: topology.TopologyTree, }, } diff --git a/pkg/topology/request_test.go b/pkg/topology/request_test.go index 2f331bf..8a4de3c 100644 --- a/pkg/topology/request_test.go +++ b/pkg/topology/request_test.go @@ -102,7 +102,7 @@ func TestPayload(t *testing.T) { Engine: topology.Engine{ Name: "slurm", Params: map[string]any{ - topology.KeyPlugin: topology.ValTopologyBlock, + topology.KeyPlugin: topology.TopologyBlock, topology.KeyBlockSizes: "30,120", }, }, diff --git a/pkg/topology/topology.go b/pkg/topology/topology.go index 852c6b6..192afc6 100644 --- a/pkg/topology/topology.go +++ b/pkg/topology/topology.go @@ -32,10 +32,10 @@ const ( KeySkipReload = "skip_reload" KeyModelPath = "model_path" - KeyPlugin = "plugin" - ValTopologyTree = "topology/tree" - ValTopologyBlock = "topology/block" - NoTopology = "no-topology" + KeyPlugin = "plugin" + TopologyTree = "topology/tree" + TopologyBlock = "topology/block" + NoTopology = "no-topology" ) // Vertex is a tree node, representing a compute node or a network switch, where diff --git a/pkg/translate/output.go b/pkg/translate/output.go index 5932336..2b9c895 100644 --- a/pkg/translate/output.go +++ b/pkg/translate/output.go @@ -29,10 +29,14 @@ import ( ) func ToGraph(wr io.Writer, root *topology.Vertex) error { - if len(root.Metadata) != 0 && root.Metadata[topology.KeyPlugin] == topology.ValTopologyBlock { - return toBlockTopology(wr, root) + if len(root.Metadata) != 0 { + if root.Metadata[topology.KeyPlugin] == topology.TopologyBlock { + return toBlockTopology(wr, root) + } else if root.Metadata[topology.KeyPlugin] == topology.TopologyTree { + return toTreeTopology(wr, root) + } } - return toTreeTopology(wr, root) + return fmt.Errorf("topology metadata not set in root node") } func printBlock(wr io.Writer, block *topology.Vertex, domainVisited map[string]int) error { @@ -110,8 +114,8 @@ func getBlockSize(domainVisited map[string]int, adminBlockSize string) string { func toBlockTopology(wr io.Writer, root *topology.Vertex) error { // traverse tree topology and when a node is reached, check within blockRoot for domain and print that domain. // keep a map of which domain has been printed - treeRoot := root.Vertices[topology.ValTopologyTree] - blockRoot := root.Vertices[topology.ValTopologyBlock] + treeRoot := root.Vertices[topology.TopologyTree] + blockRoot := root.Vertices[topology.TopologyBlock] visited := make(map[string]bool) queue := []*topology.Vertex{treeRoot} domainVisited := make(map[string]int) @@ -369,6 +373,9 @@ func GetTreeTestSet(testForLongLabelName bool) (*topology.Vertex, map[string]str } root := &topology.Vertex{ Vertices: map[string]*topology.Vertex{"S1": sw1}, + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, } return root, instance2node @@ -456,10 +463,10 @@ func GetBlockWithMultiIBTestSet() (*topology.Vertex, map[string]string) { } root := &topology.Vertex{ - Vertices: map[string]*topology.Vertex{topology.ValTopologyBlock: blockRoot, topology.ValTopologyTree: treeRoot}, + Vertices: map[string]*topology.Vertex{topology.TopologyBlock: blockRoot, topology.TopologyTree: treeRoot}, Metadata: map[string]string{ topology.KeyEngine: engines.EngineSLURM, - topology.KeyPlugin: topology.ValTopologyBlock, + topology.KeyPlugin: topology.TopologyBlock, topology.KeyBlockSizes: "3", }, } @@ -510,10 +517,10 @@ func GetBlockWithIBTestSet() (*topology.Vertex, map[string]string) { } root := &topology.Vertex{ - Vertices: map[string]*topology.Vertex{topology.ValTopologyBlock: blockRoot, topology.ValTopologyTree: treeRoot}, + Vertices: map[string]*topology.Vertex{topology.TopologyBlock: blockRoot, topology.TopologyTree: treeRoot}, Metadata: map[string]string{ topology.KeyEngine: engines.EngineSLURM, - topology.KeyPlugin: topology.ValTopologyBlock, + topology.KeyPlugin: topology.TopologyBlock, topology.KeyBlockSizes: "3", }, } @@ -548,10 +555,10 @@ func GetBlockTestSet() (*topology.Vertex, map[string]string) { } root := &topology.Vertex{ - Vertices: map[string]*topology.Vertex{topology.ValTopologyBlock: blockRoot}, + Vertices: map[string]*topology.Vertex{topology.TopologyBlock: blockRoot}, Metadata: map[string]string{ topology.KeyEngine: engines.EngineSLURM, - topology.KeyPlugin: topology.ValTopologyBlock, + topology.KeyPlugin: topology.TopologyBlock, topology.KeyBlockSizes: "3", }, } diff --git a/pkg/translate/output_test.go b/pkg/translate/output_test.go index 6e9f780..eb94f81 100644 --- a/pkg/translate/output_test.go +++ b/pkg/translate/output_test.go @@ -56,6 +56,7 @@ SwitchName=switch.1.2 Nodes=node-2 func TestToTreeTopology(t *testing.T) { v, _ := GetTreeTestSet(false) + require.Equal(t, v.Metadata[topology.KeyPlugin], topology.TopologyTree) buf := &bytes.Buffer{} err := ToGraph(buf, v) require.NoError(t, err) @@ -64,6 +65,7 @@ func TestToTreeTopology(t *testing.T) { func TestToBlockTopology(t *testing.T) { v, _ := GetBlockTestSet() + require.Equal(t, v.Metadata[topology.KeyPlugin], topology.TopologyBlock) buf := &bytes.Buffer{} err := ToGraph(buf, v) require.NoError(t, err) @@ -72,6 +74,7 @@ func TestToBlockTopology(t *testing.T) { func TestToBlockMultiIBTopology(t *testing.T) { v, _ := GetBlockWithMultiIBTestSet() + require.Equal(t, v.Metadata[topology.KeyPlugin], topology.TopologyBlock) buf := &bytes.Buffer{} err := ToGraph(buf, v) require.NoError(t, err) @@ -85,6 +88,7 @@ func TestToBlockMultiIBTopology(t *testing.T) { func TestToBlockIBTopology(t *testing.T) { v, _ := GetBlockWithIBTestSet() + require.Equal(t, v.Metadata[topology.KeyPlugin], topology.TopologyBlock) buf := &bytes.Buffer{} err := ToGraph(buf, v) require.NoError(t, err) @@ -138,8 +142,12 @@ func TestToSlurmNameShortener(t *testing.T) { }, }, }, + Metadata: map[string]string{ + topology.KeyPlugin: topology.TopologyTree, + }, } + require.Equal(t, v.Metadata[topology.KeyPlugin], topology.TopologyTree) buf := &bytes.Buffer{} err := ToGraph(buf, v) require.NoError(t, err)