Skip to content

Commit 9abf1d6

Browse files
GGN Engprod Teamkumaryoge
authored andcommitted
Project import generated by Copybara.
FolderOrigin-RevId: /usr/local/google/home/yogendrakr/copybara/temp/folder-destination3966004507874533242/.
1 parent 8fa59bf commit 9abf1d6

File tree

9 files changed

+54097
-52978
lines changed

9 files changed

+54097
-52978
lines changed

binding/solver/solver.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package solver provides a library for solving a testbed reservation request
16+
// against a given inventory of available devices and their connections.
17+
package solver
18+
19+
import (
20+
"fmt"
21+
22+
"golang.org/x/net/context"
23+
24+
"github.com/openconfig/ondatra/binding"
25+
"github.com/openconfig/ondatra/binding/portgraph"
26+
opb "github.com/openconfig/ondatra/proto"
27+
)
28+
29+
// Inventory of available binding devices and links.
30+
// This struct is provided by a specific binding implementation.
31+
type Inventory struct {
32+
DUTs []binding.DUT
33+
ATEs []binding.ATE
34+
Links map[*binding.Port]*binding.Port // Maps a port to its peer.
35+
}
36+
37+
// SolveResult contains the result of a successful testbed solve.
38+
type SolveResult struct {
39+
Assignment *portgraph.Assignment
40+
AbsNode2Dev map[*portgraph.AbstractNode]*opb.Device
41+
AbsPort2Port map[*portgraph.AbstractPort]*opb.Port
42+
ConNode2BindDev map[*portgraph.ConcreteNode]*binding.Device
43+
ConPort2BindPort map[*portgraph.ConcretePort]*binding.Port
44+
}
45+
46+
// Solve finds an assignment to the provided testbed from the given inventory.
47+
// It returns the low-level portgraph.Assignment and maps to correlate
48+
// graph elements back to the original binding and testbed elements.
49+
func Solve(ctx context.Context, tb *opb.Testbed, inv *Inventory, partial map[string]string) (*SolveResult, error) {
50+
abstractGraph, absNode2Dev, absPort2Port, err := portgraph.TestbedToAbstractGraph(tb, partial)
51+
if err != nil {
52+
return nil, fmt.Errorf("could not parse specified testbed: %w", err)
53+
}
54+
55+
superGraph, conNode2BindDev, conPort2BindPort, err := inventoryToConcreteGraph(inv)
56+
if err != nil {
57+
return nil, fmt.Errorf("could not convert inventory to concrete graph: %w", err)
58+
}
59+
60+
assignment, err := portgraph.Solve(ctx, abstractGraph, superGraph)
61+
if err != nil {
62+
return nil, fmt.Errorf("could not solve for specified testbed: %w", err)
63+
}
64+
65+
return &SolveResult{
66+
Assignment: assignment,
67+
AbsNode2Dev: absNode2Dev,
68+
AbsPort2Port: absPort2Port,
69+
ConNode2BindDev: conNode2BindDev,
70+
ConPort2BindPort: conPort2BindPort,
71+
}, nil
72+
}
73+
74+
// inventoryToConcreteGraph converts the generic Inventory struct to a portgraph.ConcreteGraph.
75+
func inventoryToConcreteGraph(inv *Inventory) (*portgraph.ConcreteGraph, map[*portgraph.ConcreteNode]*binding.Device, map[*portgraph.ConcretePort]*binding.Port, error) {
76+
cg := &portgraph.ConcreteGraph{Desc: "Generic Inventory"}
77+
conNode2BindDev := make(map[*portgraph.ConcreteNode]*binding.Device)
78+
conPort2BindPort := make(map[*portgraph.ConcretePort]*binding.Port)
79+
bindPortToConPort := make(map[*binding.Port]*portgraph.ConcretePort)
80+
81+
addDevice := func(dev binding.Device, role string) error {
82+
var dims *binding.Dims
83+
// Type assert to interface with Dims() to access device dimensions
84+
switch d := dev.(type) {
85+
case interface{ Dims() *binding.Dims }:
86+
dims = d.Dims()
87+
default:
88+
return fmt.Errorf("device %s (%T) does not provide a Dims() method", dev.Name(), dev)
89+
}
90+
91+
if dims == nil {
92+
return fmt.Errorf("device %s has nil Dims", dev.Name())
93+
}
94+
attrs := map[string]string{
95+
portgraph.RoleAttr: role,
96+
portgraph.VendorAttr: dev.Vendor().String(),
97+
portgraph.HWAttr: dev.HardwareModel(),
98+
portgraph.SWAttr: dev.SoftwareVersion(),
99+
portgraph.NameAttr: dev.Name(),
100+
}
101+
102+
var ports []*portgraph.ConcretePort
103+
// Accessing ports using the map from the Dims struct.
104+
for portID, bp := range dims.Ports {
105+
if bp == nil {
106+
return fmt.Errorf("device %s port ID %s has a nil *binding.Port", dev.Name(), portID)
107+
}
108+
pAttrs := map[string]string{
109+
portgraph.NameAttr: bp.Name,
110+
portgraph.SpeedAttr: bp.Speed.String(),
111+
portgraph.PMDAttr: bp.PMD.String(),
112+
}
113+
cp := &portgraph.ConcretePort{
114+
// Desc should be unique for each concrete port.
115+
Desc: fmt.Sprintf("%s:%s", dev.Name(), bp.Name),
116+
Attrs: pAttrs,
117+
}
118+
ports = append(ports, cp)
119+
conPort2BindPort[cp] = bp
120+
bindPortToConPort[bp] = cp
121+
}
122+
123+
node := &portgraph.ConcreteNode{
124+
Desc: dev.Name(),
125+
Ports: ports,
126+
Attrs: attrs,
127+
}
128+
cg.Nodes = append(cg.Nodes, node)
129+
conNode2BindDev[node] = &dev
130+
return nil
131+
}
132+
133+
for _, dut := range inv.DUTs {
134+
if err := addDevice(dut, portgraph.RoleDUT); err != nil {
135+
return nil, nil, nil, err
136+
}
137+
}
138+
for _, ate := range inv.ATEs {
139+
if err := addDevice(ate, portgraph.RoleATE); err != nil {
140+
return nil, nil, nil, err
141+
}
142+
}
143+
144+
edgeSet := make(map[struct {
145+
Src *portgraph.ConcretePort
146+
Dst *portgraph.ConcretePort
147+
}]bool)
148+
for srcBP, dstBP := range inv.Links {
149+
srcCP, srcOk := bindPortToConPort[srcBP]
150+
if !srcOk {
151+
return nil, nil, nil, fmt.Errorf("link source port %s not found in inventory device ports", srcBP.Name)
152+
}
153+
dstCP, dstOk := bindPortToConPort[dstBP]
154+
if !dstOk {
155+
return nil, nil, nil, fmt.Errorf("link destination port %s not found in inventory device ports", dstBP.Name)
156+
}
157+
158+
// Ensure we only add each edge once by creating a canonical key.
159+
key := struct {
160+
Src *portgraph.ConcretePort
161+
Dst *portgraph.ConcretePort
162+
}{Src: srcCP, Dst: dstCP}
163+
if srcCP.Desc > dstCP.Desc {
164+
key = struct {
165+
Src *portgraph.ConcretePort
166+
Dst *portgraph.ConcretePort
167+
}{Src: dstCP, Dst: srcCP}
168+
}
169+
170+
if !edgeSet[key] {
171+
cg.Edges = append(cg.Edges, &portgraph.ConcreteEdge{Src: key.Src, Dst: key.Dst})
172+
edgeSet[key] = true
173+
}
174+
}
175+
return cg, conNode2BindDev, conPort2BindPort, nil
176+
}

0 commit comments

Comments
 (0)