diff --git a/3tier-fabric.excalidraw b/3tier-fabric.excalidraw new file mode 100644 index 0000000..5cc61c2 --- /dev/null +++ b/3tier-fabric.excalidraw @@ -0,0 +1,1488 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "rectangle", + "version": 104, + "versionNonce": 1566371397, + "isDeleted": false, + "id": "WDusXVB_mQDscjHr36JMa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 53.48957824707031, + "y": 694.1665649414062, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 90.49478912353516, + "height": 35, + "seed": 1837404340, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "kMxWT86gEElOjeDs21ZH0" + } + ], + "updated": 1733161982239, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 73, + "versionNonce": 1709425803, + "isDeleted": false, + "id": "kMxWT86gEElOjeDs21ZH0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 66.98700332641602, + "y": 699.1665649414062, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 63.49993896484375, + "height": 25, + "seed": 1063102900, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161982239, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Host.0", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WDusXVB_mQDscjHr36JMa", + "originalText": "Host.0", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 117, + "versionNonce": 781245861, + "isDeleted": false, + "id": "ZQlkU_14pnWJDjhYfLKef", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 202.41405868530273, + "y": 694.9166259765625, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 90.49478912353516, + "height": 35, + "seed": 41170572, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "dOOQ_cPkBfIiMdozMubKP" + } + ], + "updated": 1733161982239, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 88, + "versionNonce": 1706097451, + "isDeleted": false, + "id": "dOOQ_cPkBfIiMdozMubKP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 220.08148193359375, + "y": 699.9166259765625, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 55.159942626953125, + "height": 25, + "seed": 1178961164, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161982239, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Host.1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZQlkU_14pnWJDjhYfLKef", + "originalText": "Host.1", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 151, + "versionNonce": 1179640069, + "isDeleted": false, + "id": "n5gLbetnhC9KGh8UX_dIi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 344.90363693237305, + "y": 694.4166870117188, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 90.49478912353516, + "height": 35, + "seed": 286631988, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "hbZ-ALJTvEeltkZJk52Kn" + } + ], + "updated": 1733161982239, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 122, + "versionNonce": 1876843979, + "isDeleted": false, + "id": "hbZ-ALJTvEeltkZJk52Kn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 358.1610641479492, + "y": 699.4166870117188, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 63.97993469238281, + "height": 25, + "seed": 1713013172, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161982239, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Host.2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "n5gLbetnhC9KGh8UX_dIi", + "originalText": "Host.2", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 151, + "versionNonce": 1243052133, + "isDeleted": false, + "id": "JXVKbtgmaKLZc_CdQ-xQ-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 488.0703239440918, + "y": 693.9166259765625, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 90.49478912353516, + "height": 35, + "seed": 1955146252, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "VHo_xJIw7-ZebLO-GFa0P" + } + ], + "updated": 1733161982239, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 122, + "versionNonce": 528903275, + "isDeleted": false, + "id": "VHo_xJIw7-ZebLO-GFa0P", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 501.6377487182617, + "y": 698.9166259765625, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "width": 63.35993957519531, + "height": 25, + "seed": 544213132, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161982239, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Host.3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JXVKbtgmaKLZc_CdQ-xQ-", + "originalText": "Host.3", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 161, + "versionNonce": 1617321740, + "isDeleted": false, + "id": "Sr2IumRre5QX_w3mzIq_w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 38.48959732055664, + "y": 589.3318958398979, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 35.52061716010209, + "seed": 666064820, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "VXqUvU5nQAQYD3XCytsFR" + } + ], + "updated": 1733161371061, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 237, + "versionNonce": 117172148, + "isDeleted": false, + "id": "VXqUvU5nQAQYD3XCytsFR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 52.60498936166051, + "y": 595.2959947821525, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 85.16026306152344, + "height": 23.592419275592952, + "seed": 1075903884, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161371061, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "RackSw.0", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Sr2IumRre5QX_w3mzIq_w", + "originalText": "RackSw.0", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 179, + "versionNonce": 1692310924, + "isDeleted": false, + "id": "5ypFbFce3mTwG6RLD7GSE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 188.90661513854215, + "y": 591.9257322638741, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 33.59241927559295, + "seed": 1520886028, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "4iFHPTIcfU-ZUukR2HANH" + } + ], + "updated": 1733161371061, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 257, + "versionNonce": 1410126132, + "isDeleted": false, + "id": "4iFHPTIcfU-ZUukR2HANH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 206.9564020160718, + "y": 596.9257322638741, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 77.29147338867188, + "height": 23.592419275592952, + "seed": 129949580, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161371061, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "RackSw.1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5ypFbFce3mTwG6RLD7GSE", + "originalText": "RackSw.1", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 186, + "versionNonce": 1556810764, + "isDeleted": false, + "id": "p7couTsor2DLrcfqpf34A", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 332.77449124361254, + "y": 590.8525128667727, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 34, + "seed": 233070516, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "lPaZcFQY_oZ9N4jruun_I" + } + ], + "updated": 1733161371061, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 236, + "versionNonce": 1917094580, + "isDeleted": false, + "id": "lPaZcFQY_oZ9N4jruun_I", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 346.6634428550289, + "y": 596.0563032289763, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 85.61314392089844, + "height": 23.592419275592952, + "seed": 1462126900, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161371061, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "RackSw.2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "p7couTsor2DLrcfqpf34A", + "originalText": "RackSw.2", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 198, + "versionNonce": 707111564, + "isDeleted": false, + "id": "M4j6kn6ZR0Bv37zGXgAo0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 478.16091475507255, + "y": 590.165266426859, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 34.687246573140975, + "seed": 1102244364, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9jYVB4mcTDeprt5RftD_q" + } + ], + "updated": 1733161371061, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 278, + "versionNonce": 877204532, + "isDeleted": false, + "id": "9jYVB4mcTDeprt5RftD_q", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 492.34235446463344, + "y": 595.712680075633, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 85.02816772460938, + "height": 23.592419275592952, + "seed": 576833676, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161371061, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "RackSw.3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "M4j6kn6ZR0Bv37zGXgAo0", + "originalText": "RackSw.3", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 252, + "versionNonce": 1092196660, + "isDeleted": false, + "id": "aOVFl8ejGBemhNDarLigU", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 113.78886668692348, + "y": 492.37053840907856, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 33.59241927559295, + "seed": 1270103732, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "4c6YY5QzyIp6hQgOb62Jc" + } + ], + "updated": 1733159546367, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 333, + "versionNonce": 701416460, + "isDeleted": false, + "id": "4c6YY5QzyIp6hQgOb62Jc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 133.09349822998047, + "y": 497.37053840907856, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 74.78178405761719, + "height": 23.592419275592952, + "seed": 765610036, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733159546367, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "PodSw.0", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aOVFl8ejGBemhNDarLigU", + "originalText": "PodSw.0", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 261, + "versionNonce": 442632588, + "isDeleted": false, + "id": "3M1Ivk81BNDJhxvP-_SQx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 399.4503504515719, + "y": 497.37053840907856, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 33.59241927559295, + "seed": 751926668, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "2wsiSgSoX68X5p8s4LvVB" + } + ], + "updated": 1733159572174, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 344, + "versionNonce": 1806602764, + "isDeleted": false, + "id": "2wsiSgSoX68X5p8s4LvVB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 422.6893768310547, + "y": 502.37053840907856, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 66.91299438476562, + "height": 23.592419275592952, + "seed": 1667361804, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733159575377, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "PodSw.1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "3M1Ivk81BNDJhxvP-_SQx", + "originalText": "PodSw.1", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "rectangle", + "version": 419, + "versionNonce": 875527948, + "isDeleted": false, + "id": "cjcyeVge6Xs-899jVP1Ou", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 139.9398981810641, + "y": 375.7038208797817, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 33.59241927559295, + "seed": 194412852, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "2ZE6J7eMC39QvIEL7XuxH" + } + ], + "updated": 1733159829675, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 489, + "versionNonce": 579059084, + "isDeleted": false, + "id": "2ZE6J7eMC39QvIEL7XuxH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 154.0552978515625, + "y": 380.7038208797817, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 85.16024780273438, + "height": 23.592419275592952, + "seed": 1830304436, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733159829675, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "SpineSw.0", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cjcyeVge6Xs-899jVP1Ou", + "originalText": "SpineSw.0", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "line", + "version": 61, + "versionNonce": 1618363060, + "isDeleted": false, + "id": "TdadCVfEX89L-MEBrVFF8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 98.33333587646484, + "y": 632.5, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 0.6770858764648438, + "height": 55.83331298828125, + "seed": 1440998324, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161357117, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.6770858764648438, + 55.83331298828125 + ] + ] + }, + { + "type": "line", + "version": 34, + "versionNonce": 990605964, + "isDeleted": false, + "id": "4_CjIR5aWdsKbz9qXo1e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 246.171875, + "y": 633.3333129882812, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 0.3385467529296875, + "height": 55.83331298828125, + "seed": 179460108, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161357117, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.3385467529296875, + 55.83331298828125 + ] + ] + }, + { + "type": "line", + "version": 27, + "versionNonce": 1669581876, + "isDeleted": false, + "id": "VAOoQwcP4pmD99lWkqtxE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 390.3385314941406, + "y": 630, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 0.338531494140625, + "height": 60, + "seed": 690898612, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161357117, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -0.338531494140625, + 60 + ] + ] + }, + { + "type": "line", + "version": 35, + "versionNonce": 1620394252, + "isDeleted": false, + "id": "KsH1H_8DddDB6dlB4WH0e", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 536.171875, + "y": 630, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 0.4947509765625, + "height": 56.6666259765625, + "seed": 1784892852, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161357117, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0.4947509765625, + 56.6666259765625 + ] + ] + }, + { + "type": "line", + "version": 39, + "versionNonce": 785972364, + "isDeleted": false, + "id": "ISH-JnaUA-NHkqB1IPrB9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 163.671875, + "y": 532.5, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 62.83854675292969, + "height": 48.33331298828125, + "seed": 44996108, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161324716, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -62.83854675292969, + 48.33331298828125 + ] + ] + }, + { + "type": "line", + "version": 28, + "versionNonce": 78373428, + "isDeleted": false, + "id": "m4HIsyVBbTeZ4E7Rj_XNB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 181.171875, + "y": 531.6666259765625, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 61.328125, + "height": 50.8333740234375, + "seed": 4579380, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161324716, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 61.328125, + 50.8333740234375 + ] + ] + }, + { + "type": "line", + "version": 27, + "versionNonce": 1445038860, + "isDeleted": false, + "id": "OFiSS_nfzketXMKyRHKak", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 447.8385314941406, + "y": 538.3333129882812, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 56.171875, + "height": 45, + "seed": 1409299980, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161324716, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -56.171875, + 45 + ] + ] + }, + { + "type": "line", + "version": 25, + "versionNonce": 1054440372, + "isDeleted": false, + "id": "JVYDX6zOFYyXCcqp-fS20", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 464.50518798828125, + "y": 539.1666259765625, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 63.828125, + "height": 44.16668701171875, + "seed": 338914868, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161324716, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 63.828125, + 44.16668701171875 + ] + ] + }, + { + "type": "line", + "version": 268, + "versionNonce": 295326604, + "isDeleted": false, + "id": "ayLBDbm9HAR8LL1bu4n7h", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 170, + "y": 481.6666564941406, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 26.979156494140625, + "height": 63.33331298828125, + "seed": 2142489740, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161304979, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 26.979156494140625, + -63.33331298828125 + ] + ] + }, + { + "type": "line", + "version": 142, + "versionNonce": 714046260, + "isDeleted": false, + "id": "wu4QUxLGsv6eSys5JKuPV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 441.9791564941406, + "y": 419.1666259765625, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 16.51043701171875, + "height": 69.16668701171875, + "seed": 1736923700, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161304979, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 16.51043701171875, + 69.16668701171875 + ] + ] + }, + { + "type": "rectangle", + "version": 525, + "versionNonce": 1779897780, + "isDeleted": false, + "id": "x0XKRUKatodZtHq8EZij8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0.004542919067668905, + "x": 379.1221529723239, + "y": 376.03713386806294, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 113.39104714373117, + "height": 33.59241927559295, + "seed": 1314810124, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "ye4FWC2HPdT68mCg5jW9n" + } + ], + "updated": 1733159883681, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 597, + "versionNonce": 690077708, + "isDeleted": false, + "id": "ye4FWC2HPdT68mCg5jW9n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0.004542919067668905, + "x": 397.17194747924805, + "y": 381.03713386806294, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 77.29145812988281, + "height": 23.592419275592952, + "seed": 725968780, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733159938465, + "link": null, + "locked": false, + "fontSize": 18.87393542047436, + "fontFamily": 1, + "text": "SpineSw.1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "x0XKRUKatodZtHq8EZij8", + "originalText": "SpineSw.1", + "lineHeight": 1.25, + "baseline": 17 + }, + { + "type": "line", + "version": 39, + "versionNonce": 614466060, + "isDeleted": false, + "id": "uby9pgZEK4qOee0HdpbuL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 427.00517654418945, + "y": 420, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 234.5052032470703, + "height": 62.5, + "seed": 1094527796, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161304979, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -234.5052032470703, + 62.5 + ] + ] + }, + { + "type": "line", + "version": 35, + "versionNonce": 1994519732, + "isDeleted": false, + "id": "7Ju0G9rCDZ1KcoNeovj6u", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 208.67184829711914, + "y": 420, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 234.6614532470703, + "height": 69.16665649414062, + "seed": 319208460, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1733161304979, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 234.6614532470703, + 69.16665649414062 + ] + ] + }, + { + "type": "text", + "version": 78, + "versionNonce": 254027188, + "isDeleted": false, + "id": "b4RNMTYPgy-mZJYe2znd0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 4.322902679443359, + "y": 651.6666259765625, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 73.83993530273438, + "height": 25, + "seed": 727956276, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161357117, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "100gbps", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "100gbps", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "text", + "version": 143, + "versionNonce": 1381255564, + "isDeleted": false, + "id": "aM-41jk6tix2-dxOQLOMn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 8.569595336914062, + "y": 536.8333435058594, + "strokeColor": "#1971c2", + "backgroundColor": "transparent", + "width": 82.65992736816406, + "height": 25, + "seed": 1181664180, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161324716, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "200gbps", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "200gbps", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "text", + "version": 324, + "versionNonce": 883713765, + "isDeleted": false, + "id": "mnVS4kKxU2s3YfJzN9G1L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 11.32107162475586, + "y": 434.6666564941406, + "strokeColor": "#2f9e44", + "backgroundColor": "transparent", + "width": 80.85446166992188, + "height": 24.89579554406122, + "seed": 1099122572, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733161951328, + "link": null, + "locked": false, + "fontSize": 19.916636435248975, + "fontFamily": 1, + "text": "400gbps", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "400gbps", + "lineHeight": 1.25, + "baseline": 17 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/3tier-fabric.png b/3tier-fabric.png new file mode 100644 index 0000000..ecc3d7e Binary files /dev/null and b/3tier-fabric.png differ diff --git a/Makefile b/Makefile index 9883b33..03c6338 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,10 @@ build: ## compile all .proto files and generate artifacts mkdir -p $(GENERATED_DIR) python3 -m grpc_tools.protoc \ --proto_path=./protos \ - --python_out=$(GENERATED_DIR) --pyi_out=$(GENERATED_DIR) \ - et_def.proto infra.proto + --python_out=$(GENERATED_DIR) \ + --pyi_out=$(GENERATED_DIR) \ + --grpc_python_out=$(GENERATED_DIR) \ + et_def.proto infra.proto bind.proto service.proto python3 -m pip uninstall -y keysight-chakra python3 setup.py bdist_wheel python3 -m pip install --no-cache . diff --git a/README.md b/README.md index 8abdacf..024494e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,197 @@ -# Infrastructure as a graph +# Infrastructure as a Graph -Predefined Infrastructure as a graph includes the following: +Create infrastructure as a graph using messages from infra.proto. The messages allow a user to easily create logical infrastructure as vertexes and edges and scale it up and scale it out without duplicating content. -- a Generic host package -- a ZionEx host package -- a generic rack switch package -- a generic pod switch package -- tests showing how to create infrastructure +Submit it to the infrastructure `create` API to validate and store the data in a cogdb inmemory data store. + +Use the `query` api to find paths from npu to npu. + +![Process](process.png) + +Given the following logical `3 tier fabric` + +![3tierfabric](3tier-fabric.png) + +the following steps illustrate how to create infrastructure as a graph consisting of `dgxa100` and `tomahawk3` devices as hosts and switches: + +- [Create devices](#create-a-device-inventory) +- [Scale out the devices](#create-device-instances) +- [Connect the devices](#connect-device-instances) +- [Optionally extend the graph](#extending-infrastructure-as-a-graph) +- [Validate the graph](#validate-the-infrastructure) +- [Query the graph](#query-the-infrastructure) + +## Create a Device Inventory + +A device inventory is designed to define the device components, links and connections once and that device can subsequently be reused in a `DeviceInstance` message to scale out the device under different names. + +> Note that the entire device does not need to be described in full detail. The level of device detail should be dictated by the needs of the application. + +- use the `Component` message to define individual components (vertexes) that are present in a device +- use the `Component.count` field to scale up the number of components in the device +- use the `Link` message to define different links within the device +- use the `Device` message to contain `Component` and `Link` messages +- use the `Device.connections` field to connect components (vertexes) to each other with an associated link to form an edge + - the format of a `connections` string is described in the infra.proto file + +### DgxA100 device + +```yaml +infrastructure: + inventory: + devices: + - name: dgxa100 + components: + - name: a100 + count: 8 + npu: + memory: + - name: nvsw + count: 6 + switch: + nvswitch: + - name: nic + count: 8 + nic: + ethernet: + - name: pciesw + count: 4 + switch: + pcie: + links: + - name: nvlink + description: NVLink 3.0, 25GBs/50GBs bidirectional + bandwidth: + gBs: 50 + - name: pcie + description: PCI Express x16 (gen 4.0) + bandwidth: + gBs: 24.7 + connections: + - a100.[:].nvlink.nvsw.[:].MTM + - a100.[0:1].pcie.pciesw.0.MTO + - nic.[0:1].pcie.pciesw.0.MTO +``` + +### Tomahawk3 device + +```yaml +infrastructure: + inventory: + devices: + - name: th3 + components: + - name: asic + count: 1 + custom: + memory: + - name: nic + count: 32 + nic: + ethernet: + links: + - name: mii + custom: + bandwidth: + connections: + - nic.[:].mii.asic.0 +``` + +## Create Device Instances + +Scale out the infrastructure by using the `DeviceInstance` message and appending instances of them to the `Infrastructure.device_instances` field. + +- the `DeviceInstance.name` field is a unique key that enables reuse of device inventory under different names +- the `DeviceInstance.device` field is a name that must exist in the `Infrastructure.inventory.devices` map key + +```yaml +infrastructure: + device_instances: + - name: host + device: dgxa100 + count: 4 + - name: racksw + device: th3 + count: 4 + - name: podsw + device: th3 + count: 2 + - name: spinesw + device: th3 + count: 2 +``` + +## Connect Device Instances + +Connect device instances using the `Infrastructure.connections` field to create the `3 tier fabric` as a graph. + +```yaml +infrastructure: + connections: + - host.0.nic.[0:8].eth.racksw.0.nic.[0:8].OTO + - host.1.nic.[0:8].eth.racksw.0.nic.[0:8].OTO + - host.2.nic.[0:8].eth.racksw.0.nic.[0:8].OTO + - host.3.nic.[0:8].eth.racksw.0.nic.[0:8].OTO + - racksw.0.nic.[8:11].eth.podsw.0.nic.[0:8].OTO + - racksw.1.nic.[8:11].eth.podsw.0.nic.[0:8].OTO + - racksw.2.nic.[8:11].eth.podsw.1.nic.[0:8].OTO + - racksw.3.nic.[8:11].eth.podsw.1.nic.[0:8].OTO +``` + +## Extending Infrastructure as a Graph + +Use the Bindings message in bind.proto to extend the logical infrastructure by adding any data that is outside the scope of the graph. + +This is done by binding logical endpoints to any data such as: + +### Global Metadata + +```yaml +bindings: + - targets: + - infrasrtucture: Metadata + data: + name: DeviceTypes + description: Key value metadata map of a user specified device type to an infrastructure inventory device name + value: + - @type: type.googleapis.com/google.protobuf.Struct + - device_types: + - key: host + value: dgxa100 + - key: switch + value: th3 +``` + +### Physical Configuration + +```yaml +bindings: + - targets: + - device_instance: racksw + - device_instance_index: podsw.0 + - device_instance: spinesw + data: + name: OpenConfigInterface + description: Switch configuration + value: + - @type: type.googleapis.com/google.protobuf.Struct + - config: + - type: ... + - mtu: ... + - loopback-mode: ... + - enabled: ... +``` + +### Application Configuration + +```yaml + +``` + +## Validate the Infrastructure + +Explodes any connection shortcuts and validates the overall graph to ensure referential integrity. + +## Query the Infrastructure + +Use queries to extract paths. diff --git a/dgxa100-schematic.png b/dgxa100-schematic.png new file mode 100644 index 0000000..659e971 Binary files /dev/null and b/dgxa100-schematic.png differ diff --git a/process.excalidraw b/process.excalidraw new file mode 100644 index 0000000..06a1ea5 --- /dev/null +++ b/process.excalidraw @@ -0,0 +1,732 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "id": "7fux8l1t6HhuFV5aafTHM", + "type": "rectangle", + "x": 258.33331298828125, + "y": 177.5, + "width": 160, + "height": 60.83332824707031, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 758253418, + "version": 49, + "versionNonce": 177360502, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "PXToycrx8O1f13mxCW8Px" + }, + { + "id": "4dFeHKKi_-pKyoFwGlCKe", + "type": "arrow" + } + ], + "updated": 1733179606436, + "link": null, + "locked": false + }, + { + "id": "PXToycrx8O1f13mxCW8Px", + "type": "text", + "x": 286.94336700439453, + "y": 195.41666412353516, + "width": 102.77989196777344, + "height": 25, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1780082102, + "version": 42, + "versionNonce": 1498971114, + "isDeleted": false, + "boundElements": null, + "updated": 1733179606436, + "link": null, + "locked": false, + "text": "infra.proto", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 18, + "containerId": "7fux8l1t6HhuFV5aafTHM", + "originalText": "infra.proto", + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 143, + "versionNonce": 1668091830, + "isDeleted": false, + "id": "d891p9WwX6Yx4vHKqiuTg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 483.16668701171875, + "y": 184.58336639404297, + "strokeColor": "#f08c00", + "backgroundColor": "transparent", + "width": 160, + "height": 60.83332824707031, + "seed": 1758291114, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "fq_bggxV-s7Qv5D4QcXrq" + }, + { + "id": "05bk05SMnR4Do4hY9DQ_j", + "type": "arrow" + } + ], + "updated": 1733179606436, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 151, + "versionNonce": 561344170, + "isDeleted": false, + "id": "fq_bggxV-s7Qv5D4QcXrq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 516.8367385864258, + "y": 202.50003051757812, + "strokeColor": "#f08c00", + "backgroundColor": "#ffffff", + "width": 92.65989685058594, + "height": 25, + "seed": 1808744298, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733179606436, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "bind.proto", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d891p9WwX6Yx4vHKqiuTg", + "originalText": "bind.proto", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 148, + "versionNonce": 1454892458, + "isDeleted": false, + "id": "1lo0_2vBdJgaempMg6zke", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 366.2500305175781, + "y": 309.0833740234375, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 132.5, + "height": 52.5, + "seed": 1581471414, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "IxSqKqoSjueTiLuFchBV0" + }, + { + "id": "4dFeHKKi_-pKyoFwGlCKe", + "type": "arrow" + }, + { + "id": "05bk05SMnR4Do4hY9DQ_j", + "type": "arrow" + }, + { + "id": "SzHj5OcYijsW34eNkCzXg", + "type": "arrow" + } + ], + "updated": 1733179655771, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 111, + "versionNonce": 2010163050, + "isDeleted": false, + "id": "IxSqKqoSjueTiLuFchBV0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 399.92005920410156, + "y": 322.8333740234375, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 65.15994262695312, + "height": 25, + "seed": 1683165174, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733179601919, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "create", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1lo0_2vBdJgaempMg6zke", + "originalText": "create", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "type": "rectangle", + "version": 152, + "versionNonce": 525626090, + "isDeleted": false, + "id": "If9VyWXQh74nHrA_8DbEW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 362.0833435058594, + "y": 429.91668701171875, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 132.5, + "height": 52.5, + "seed": 1932856810, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "CkIXgvNfH7JwZAskETBht" + }, + { + "id": "SzHj5OcYijsW34eNkCzXg", + "type": "arrow" + }, + { + "id": "R4FP6YYmcpmu9MmJAgpmC", + "type": "arrow" + } + ], + "updated": 1733179659883, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 108, + "versionNonce": 1075543466, + "isDeleted": false, + "id": "CkIXgvNfH7JwZAskETBht", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 388.1333770751953, + "y": 443.66668701171875, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 80.39993286132812, + "height": 25, + "seed": 904904874, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733179659883, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "validate", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "If9VyWXQh74nHrA_8DbEW", + "originalText": "validate", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "id": "UUWFhZA_Z2A8yBsPt_TQ0", + "type": "ellipse", + "x": 442.5, + "y": 594.1666259765625, + "width": 132.5, + "height": 99.16668701171875, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 75701802, + "version": 123, + "versionNonce": 1091082090, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "RQTpHONv6RMoOBtjAqxEe" + }, + { + "id": "R4FP6YYmcpmu9MmJAgpmC", + "type": "arrow" + }, + { + "id": "3M4I4D6FerZvUv-YqtA0G", + "type": "arrow" + } + ], + "updated": 1733179665619, + "link": null, + "locked": false + }, + { + "id": "RQTpHONv6RMoOBtjAqxEe", + "type": "text", + "x": 482.56420229668424, + "y": 631.1892510555267, + "width": 52.67994689941406, + "height": 25, + "angle": 0, + "strokeColor": "#1971c2", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1645914858, + "version": 104, + "versionNonce": 1670781482, + "isDeleted": false, + "boundElements": null, + "updated": 1733179665619, + "link": null, + "locked": false, + "text": "cogdb", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 18, + "containerId": "UUWFhZA_Z2A8yBsPt_TQ0", + "originalText": "cogdb", + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 274, + "versionNonce": 21353258, + "isDeleted": false, + "id": "JEnVJyaI1sEV5Zsw6aEvo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 556.25, + "y": 429.58331298828125, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 132.5, + "height": 52.5, + "seed": 1811150582, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "kaRhTwkla8A9HaOwIWYyw" + }, + { + "id": "3M4I4D6FerZvUv-YqtA0G", + "type": "arrow" + } + ], + "updated": 1733179662951, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 246, + "versionNonce": 1748360682, + "isDeleted": false, + "id": "kaRhTwkla8A9HaOwIWYyw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 596.9000244140625, + "y": 443.33331298828125, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "width": 51.199951171875, + "height": 25, + "seed": 1803921462, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1733179662951, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "query", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JEnVJyaI1sEV5Zsw6aEvo", + "originalText": "query", + "lineHeight": 1.25, + "baseline": 18 + }, + { + "id": "4dFeHKKi_-pKyoFwGlCKe", + "type": "arrow", + "x": 343.33331298828125, + "y": 249.16665649414062, + "width": 75, + "height": 52.5, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1589385398, + "version": 22, + "versionNonce": 1909020854, + "isDeleted": false, + "boundElements": null, + "updated": 1733179614524, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 75, + 52.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "7fux8l1t6HhuFV5aafTHM", + "focus": 0.4368369544633284, + "gap": 10.833328247070312 + }, + "endBinding": { + "elementId": "1lo0_2vBdJgaempMg6zke", + "focus": 0.32702258531895434, + "gap": 7.416717529296875 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "05bk05SMnR4Do4hY9DQ_j", + "type": "arrow", + "x": 556.6666259765625, + "y": 257.5, + "width": 109.99996948242188, + "height": 42.5, + "angle": 0, + "strokeColor": "#f08c00", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1010506282, + "version": 28, + "versionNonce": 1822307754, + "isDeleted": false, + "boundElements": null, + "updated": 1733179614524, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -109.99996948242188, + 42.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d891p9WwX6Yx4vHKqiuTg", + "focus": -0.6520682208424431, + "gap": 12.083305358886719 + }, + "endBinding": { + "elementId": "1lo0_2vBdJgaempMg6zke", + "focus": -0.5759279205090639, + "gap": 9.0833740234375 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "SzHj5OcYijsW34eNkCzXg", + "type": "arrow", + "x": 433.45069716544754, + "y": 370.83331298828125, + "width": 1.9153950526961125, + "height": 50.8333740234375, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 151957290, + "version": 166, + "versionNonce": 2057542442, + "isDeleted": false, + "boundElements": null, + "updated": 1733179659883, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1.9153950526961125, + 50.8333740234375 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "1lo0_2vBdJgaempMg6zke", + "focus": 0.012070625414872272, + "gap": 9.24993896484375 + }, + "endBinding": { + "elementId": "If9VyWXQh74nHrA_8DbEW", + "focus": 0.12392647624314666, + "gap": 8.25 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "R4FP6YYmcpmu9MmJAgpmC", + "type": "arrow", + "x": 432.2048593651876, + "y": 492.5000305175781, + "width": 55.99927020157617, + "height": 94.64854329066407, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 660166774, + "version": 242, + "versionNonce": 42649514, + "isDeleted": false, + "boundElements": null, + "updated": 1733179673158, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 55.99927020157617, + 94.64854329066407 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "If9VyWXQh74nHrA_8DbEW", + "focus": 0.21551822773125834, + "gap": 10.083343505859375 + }, + "endBinding": { + "elementId": "UUWFhZA_Z2A8yBsPt_TQ0", + "focus": 0.17863062920204015, + "gap": 9.219957888062659 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "3M4I4D6FerZvUv-YqtA0G", + "type": "arrow", + "x": 618.8025084509942, + "y": 489.9999694824219, + "width": 73.44443676666413, + "height": 97.86158755969927, + "angle": 0, + "strokeColor": "#2f9e44", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 529532522, + "version": 239, + "versionNonce": 978656490, + "isDeleted": false, + "boundElements": null, + "updated": 1733179670459, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -73.44443676666413, + 97.86158755969927 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JEnVJyaI1sEV5Zsw6aEvo", + "focus": -0.25531393158554894, + "gap": 7.916656494140625 + }, + "endBinding": { + "elementId": "UUWFhZA_Z2A8yBsPt_TQ0", + "focus": -0.07022119766964094, + "gap": 13.28874529725752 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/process.png b/process.png new file mode 100644 index 0000000..84ec537 Binary files /dev/null and b/process.png differ diff --git a/protos/bind.proto b/protos/bind.proto new file mode 100644 index 0000000..d2c56bf --- /dev/null +++ b/protos/bind.proto @@ -0,0 +1,128 @@ +// bind.proto +// +// A data model to describe binding infrastructure paths to external +// application information. +// +// Some examples of binding third party information to infrastructure paths: +// +// binding = Binding( +// device_instance="pod_switch.0", +// data=Data( +// name="routing", +// value="routing configuration information in a third party format")) +// +// binding = Binding( +// device_instance_index_component_index="host.0.nic.0", +// data=Data( +// name="mellanox_config", +// value="additional nic configuration information in a third party format")) +// +// binding = Binding( +// infrastructure="ranks", +// data=Data( +// name="rank_to_host_map", +// value="rank to host mapping in a third party format")) +// + +syntax = "proto3"; + +package bind; + +import "google/protobuf/any.proto"; + +// Data message allows a user to provide data outside of the scope of the +// infrastructure graph. +message Data { + // Use this field to provide descriptive information about the message + // that is packed into the value field. + string name = 1; + + // The value field can be used to store external information outside the + // scope of the infra.proto messages. + // + // In addition to custom messages any wellknown type can be set into an + // Any message. See the following reference for details: + // https://protobuf.dev/reference/protobuf/google.protobuf/ + // + // The following is how to serialize a proto message into the Data.value field + // + // custom_msg = CustomMessage() + // any_message = Any() + // any_message.Pack(custom_msg) + // data = Data(name="a custom message", value=custom_msg) + // + // The following is how to deserialize the contents of Data.value + // + // custom_msg = CustomMessage() + // if data.value.Is(CustomMessage.DESCRIPTOR): + // data.value.Unpack(custom_msg) + google.protobuf.Any value = 2; +} + +// The BindTarget message +message BindTarget { + // The location of infrastructure that matches the binding type format + oneof infrastructure_path { + // the binding is global to the infrastructure and the value provided here + // is for informational purposes only + string infrastructure = 1; + + // binding is specific to an Infrastructure.inventory.device.name + // example: dgx + string device = 2; + + // binding is specific to an Infrastructure.inventory.device.name + // example: dgx.npu + string device_component = 3; + + // binding is specific to an Infrastructure.inventory.device.name + // example: dgx.npu.0 + string device_component_index = 4; + + // binding is specific to an instance of + // Infrastructure.device_instances.name + // example: host + string device_instance = 5; + + // binding is specific to an instance of + // Infrastructure.device_instances.name + // and an index < Infrastructure.device_instances.count + // example: host.1 + string device_instance_index = 6; + + // binding is specific to an instance of + // Infrastructure.device_instances.name + // and an index < Infrastructure.device_instances.count + // and an Infrastructure.inventory.device.component.name + // example: host.1.npu + string device_instance_index_component = 7; + + // binding is specific to an instance of + // Infrastructure.device_instances.name + // and an index < Infrastructure.device_instances.count + // and an Infrastructure.inventory.device.component.name + // and an index < Infrastructure.inventory.device.component.name.index + // example: host.1.npu.0 + string device_instance_index_component_index = 8; + } +} + +// The Binding message offers the option of binding different types of logical +// Infrastructure endpoints to any type of user defined data. +// +// The format allows for data to be applied at a macro level such as +// all devices or at a micro level such as an individual component in a +// specific device instance. +message Binding { + // targets is a list of BindTarget messages that share the same + // user defined information stored in the data field + repeated BindTarget targets = 1; + + // the data field accomodates any type of user defined information + Data data = 100; +} + +message Bindings { + // A list of user defined information for specific endpoints + repeated Binding bindings = 1; +} diff --git a/protos/infra.proto b/protos/infra.proto index 2d97017..62be9b8 100644 --- a/protos/infra.proto +++ b/protos/infra.proto @@ -1,18 +1,28 @@ // infra.proto -// An abstract data model for describing static infrastructure as a graph. +// +// A data model for describing infrastructure as a graph. +// +// NOTES: +// - use of optional is to allow for a better presence check, all optional +// fields must be filled in. For additional details on why optional is being +// used review this article https://protobuf.dev/programming-guides/proto3/#field-labels +// syntax = "proto3"; package infra; -import "et_def.proto"; +import "google/protobuf/any.proto"; enum MemoryType { MEM_UNSPECIFIED = 0; + // random access memory MEM_RAM = 1; + // high bandwidth memory interface for 3D stacked sync dynamic random-access memory MEM_HBM = 2; + // memory that uses compute express link interconnect to the cpu MEM_CXL = 3; } @@ -54,7 +64,7 @@ message Custom { message Switch { oneof type { Pcie pcie = 1; - NvLink nvlink = 2; + NvLink nvswitch = 2; Custom custom = 3; } } @@ -62,36 +72,28 @@ message Switch { // Component describes a number of components that share a specific type message Component { // the name of the component - string name = 1; + optional string name = 1; + // the number of this type of component - uint32 count = 2; + optional uint32 count = 2; + // the type of component oneof type { // a custom component that does not fall into one of the other messages within this type CustomComponent custom = 10; + // cpu component configuration Cpu cpu = 11; + // npu component configuration Npu npu = 12; + // nic component configuration Nic nic = 13; + // switch component configuration Switch switch = 14; } - // a list of custom attributes to allow for the specification of information - // not provided by the current message structure - repeated ChakraProtoMsg.AttributeProto attributes = 100; -} - -enum LinkType { - LINK_UNSPECIFIED = 0; - LINK_CUSTOM = 1; - LINK_ETHERNET = 2; - LINK_PCIE = 3; - LINK_NVLINK = 4; - LINK_QPI = 5; - LINK_UPI = 6; - LINK_INFINIBAND = 7; } message Bandwidth { @@ -105,170 +107,122 @@ message Bandwidth { } } -message Latency { - oneof type { - // milliseond latency - uint64 ms = 1; - // microsecond latency - uint64 us = 2; - // nanonsecond latency - uint64 ns = 3; - } -} - // Link describes a link between Components message Link { // name of the link - string name = 1; - // type of link - LinkType type = 2; - // NOTE: the following fields are currently being discussed - // and are subject to change - Bandwidth bandwidth = 10; - Latency latency = 11; - // a list of custom attributes to allow for the specification of information - // not provided by the current message structure - repeated ChakraProtoMsg.AttributeProto attributes = 100; -} + optional string name = 1; -// ComponentLink describes the link of specific components inside a device -message ComponentLink { - // Device.components.name - string c1 = 1; - // 0 based index < Device.components.count - uint32 c1_index = 2; - // Link.name - string link = 3; - // Device.components.name - string c2 = 4; - // 0 based index < Device.components.count - uint32 c2_index = 5; -} + // type of link + optional string description = 2; -message ComponentConnection { - oneof type { - // link is the explicit description of the connection of - // two unique Components in a Device - ComponentLink link = 1; - // string is a short form of the ComponentLink message which is a - // a concatentation of the ComponentLink field values separated by a comma - // at the link name - // .,,. - // e.g., cpu.0,pcie,nic.0 - string string = 2; - } + // Describes the bandwidth of the link + Bandwidth bandwidth = 10; } // Device contains collections of components and links and the connections // between those components message Device { // the name of the device - string name = 1; + optional string name = 1; + // collection of unique components in the device + // The key must be the Component.name which is used to guard against duplicates + // and will be checked as part of Service.Validation rpc map components = 3; + // collection of unique links in the device + // The key must be the Link.name which is used to guard against duplicates + // and will be checked as part of Service.Validation rpc map links = 4; - // a list of connections that describe how Components are connected to each - // other in the Device - repeated ComponentConnection connections = 5; -} - -// DeviceLink describes the connection between components of different devices -// A different device can be the same name of a device but the index must not -// be the same -message DeviceLink { - // an Infrastructure.devices[name] - string d1 = 1; - // 0 based index < Infrastructure.devices[d1].count - uint32 d1_index = 2; - // an Infrastructure.devices[d1].components[name] - string c1 = 3; - // 0 based index < Infrastructure.devices[d1].components[c1].count - uint32 c1_index = 4; - // Infrastructure.links[name] - string link = 5; - // an Infrastructure.devices[name] != d1 - string d2 = 6; - // 0 based index < Infrastructure.devices[d2].count - uint32 d2_index = 7; - // a Infrastructure.devices[d2].components[name] - string c2 = 8; - // a 0 based index < Infrastructure.devices[d2].components[c2].count - uint32 c2_index = 9; -} - -message DeviceConnection { - oneof type { - // connection detail is an explicit way of describing a connection between - // two unique Devices - DeviceLink link = 1; - // connection is a short form of the DeviceLink message which is a - // a concatentation of the DeviceLink field values separated by a comma - // .,,... - // e.g., host.0.nic.0,eth,rack.0.nic.0 - string string = 2; - } -} - -// DeviceCount allows for specifying multiple instances of a Device in the -// Infrastructure message -message DeviceCount { - // The number of these devices in the infrastructure - uint32 count = 1; - // the device definition - Device device = 2; -} -// CustomFabric is an abstract collection of messages used to describe a fabric -// consisting of a collection of devices, links and connections between those devices -message CustomFabric { - // the name of the custom infrastructure - string name = 1; - // a map of all the different Device messages and their counts that exist - // in the custom fabric - map devices = 2; - // a map of all the different links that exist in the custom fabric - map links = 3; - // a list of the DeviceConnection messages that describe how Devices are - // connected to each other in the custom fabric - repeated DeviceConnection connections = 4; -} - -// ClosFabric is a structured collection of host/rack/pod/spine devices, -// links and connections between those devices -message ClosFabric { - // describes the lowest tier of switches - DeviceCount rack_switches = 2; - // describes the middle tier of switches - DeviceCount pod_switches = 3; - // describes the highest tier of switches - DeviceCount spine_switches = 4; - // describes the links used in the connections field - map links = 5; - // describes the connections between the switch devices - repeated DeviceConnection connections = 6; -} - -//describes a collection of host objects -message Hosts { - // definition of the host devices - DeviceCount devices = 1; -} - -// Infrastructure is a combination of host devices, fabric devices, -// and the connections between them + // a list of connections that describe how Components are connected to each + // other in a single Device + // + // format: The following pieces of information each separated by a "." + // + // - name = Component.name + // - index < Component.count + // - name = Link.name from one of Component.links + // - name = Component.name + // - index < Component.count + // + // examples: + // nic.0.pcie.cpu.0 + // npu.0.pcie.nvswitch.0 + // asic.0.mii.nic.0 + // + repeated string connections = 5; +} + +message DeviceInstances { + // the name of the device instances in the topology + // it should be used to categorize the devices + // for example it can be Dgx1Host, ZionexHost, RackSwitch, PodSwitch, + // SpineSwitch etc. + optional string name = 1; + + // the name of an actual device that exists in the + // Infrastructure.inventory.devices field + // this allows for a Device to be reused. + optional string device = 2; + + // the number of instances of the device in the infrastructure under this name + // must be >= 1 + optional uint32 count = 3; +} + +// The Inventory message is a collection of unique devices and links present +// in the infrastructure. +// The devices and links in the inventory are meant to be reused in +// DeviceInstance, DeviceLink, ConnectionLink messages +message Inventory { + // A collection of all unique types of devices in the infrastructure + // Uniqueness is determined by the Device.name field. + // This list is not an instance list instead use the DeviceInstances message + // to create an instance of a Device and to scale it to the count present + // in your infrastructure. + map devices = 1; + + // A collection of all unique types of links in the infrastructure. + // These links can be reused multiple times when creating connections + // between devices. + // The key must be the Link.name which is used to guard against duplicates + // and will be checked as part of Service.Validation rpc + map links = 2; +} + +// The Infrastructure message establishes an inventory of devices and links, +// instances of the inventory, connectivity between those instances and +// any custom user information about devices, components, links and instances. +// +// The Infrastructure can be as small as a single host connected to a switch or +// as large as multiple data centers each with different fabrics that are +// interconnected. message Infrastructure { - // describes the collection of host objects - Hosts hosts = 1; - - // describes the fabric and the devices in it - oneof fabricType { - CustomFabric custom_fabric = 2; - ClosFabric clos_fabric = 3; - } - - //describes the links used to connect the host devices and the fabric - map links = 4; - // describes the connections between the host devices and the fabric - repeated DeviceConnection connections = 5; + // The inventory of devices and links present in the infrastructure + Inventory inventory = 1; + + // A map of the device instances that represents the total number of devices + // in the infrastructure. + // Use this to scale out infrastructure + // The must be the DeviceInstances.name which is used to guard against duplicates + // and will be checked as part of Service.Validation rpc + map device_instances = 2; + + // format: The following pieces of information each separated by a "." + // + // - name = DeviceInstance.name + // - index < DeviceInstance.count + // - name = Component.name + // - index < Component.count + // - name = Link.name from one of Infrastructure.inventory.links + // - name = DeviceInstance.name + // - index < DeviceInstance.count + // - name = Component.name + // - index < Component.count + // + // examples: + // host.0.nic.0.100gpbs.racksw.0.nic.0 + // + repeated string connections = 3; } diff --git a/protos/service.proto b/protos/service.proto new file mode 100644 index 0000000..bed96f2 --- /dev/null +++ b/protos/service.proto @@ -0,0 +1,51 @@ +// service.proto +// +// Service and rpcs for infrastructure and bindings + +syntax = "proto3"; + +package service; + +import "infra.proto"; +import "bind.proto"; + +message ValidationRequest { + infra.Infrastructure infrastructure = 1; + bind.Bindings bindings = 2; +} + +message ValidationError { + oneof type { + // optional fields must be set to a value + string optional = 1; + + // oneof fields must have one field set + string oneof = 2; + + // map keys must match a name field in the object + string map = 3; + + // referential integrity check failed + // the following cases fall under this type: + // connection structure is incorrectly formatted + // connection pieces are not present in the inventory or device instances + // binding infrastructure path is incorrectly formatted + // binding infrastructure path pieces are not present in the infrastructure + // inventory + string referential_integrity = 4; + + // scale up / scale out count + string count = 5; + } +} + +message ValidationResponse { + repeated ValidationError errors = 1; + repeated string warnings = 2; + repeated string info = 3; +} + +service InfraService { + // Validate rpc validates both infra and binding messages + rpc Validate(ValidationRequest) returns (ValidationResponse); +} \ No newline at end of file diff --git a/src/builders.py b/src/builders.py deleted file mode 100644 index a6a1d49..0000000 --- a/src/builders.py +++ /dev/null @@ -1,411 +0,0 @@ -"""Utility classes for constructing infrastructure - -Features: -- extended functionality for finding nodes, building inter-package adjacencies, - calculating paths etc -""" - -from typing import List, Literal, Union, Type, Tuple -from abc import ABC, abstractmethod -import google.protobuf.json_format -import yaml - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import utilities.Utilities as Utilities -else: - from .generated import infra_pb2 as infra - from .utilities import Utilities - - -class DeviceBuilder(ABC): - COMPONENT_TYPES = Literal["cpu", "npu", "nic"] - - def __init__(self): - self._device: infra.Device = None - - @property - def device(self) -> infra.Device: - """Returns the infra_pb2.Device""" - return self._device - - def serialize(self, type: Literal["yaml", "json", "dict"]) -> Union[str, dict]: - """Returns the infra_pb2.Device as the specified serialization type""" - if type == "dict": - return google.protobuf.json_format.MessageToDict(self._device) - elif type == "yaml": - return yaml.dump(google.protobuf.json_format.MessageToDict(self._device)) - elif type == "json": - return google.protobuf.json_format.MessageToJson(self._device) - - @property - @abstractmethod - def name(self) -> str: - pass - - @property - @abstractmethod - def description(self) -> str: - pass - - @property - @abstractmethod - def port_up_component(self) -> infra.Component: - pass - - def _add_component_link(self, c1, c1_index, link_name, c2, c2_index): - connection = infra.ComponentConnection( - link=infra.ComponentLink( - c1=c1, - c1_index=c1_index, - link=link_name, - c2=c2, - c2_index=c2_index, - ) - ) - self._device.connections.extend([connection]) - - def _create_distributed_intra_package_adjacencies( - self, - c1: infra.Component, - link: infra.Link, - c2: infra.Component, - ): - """Builds adjacencies - - Ensure max component (c1.count, c2.count) is evenly distributed across - the min component - ie c1.count = 16, c2.count = 8 - [0,1] -> [0] - [2,3] -> [1] - """ - assert isinstance(c1, infra.Component) - assert isinstance(link, infra.Link) - assert isinstance(c2, infra.Component) - connections = [] - max = c1 if c1.count >= c2.count else c2 - min = c2 if max.name == c1.name else c1 - step = int(max.count / min.count) - min_idxs = [] - for idx in range(min.count): - min_idxs.extend([idx] * step) - cmp_max_min = Utilities.get_indexes(c1, c2) - for c1_idx, c2_idx in zip(cmp_max_min[0][1], cmp_max_min[1][1]): - connections.append( - infra.ComponentConnection( - link=infra.ComponentLink( - c1=max.name, - c1_index=c1_idx, - link=link.name, - c2=min.name, - c2_index=c2_idx, - ) - ) - ) - self._device.connections.extend(connections) - - def _create_one_to_one_intra_package_adjacencies( - self, - c1: infra.Component, - link: infra.Link, - c2: infra.Component, - ): - connections = [] - for c1_idx, c2_idx in zip(range(c1.count), range(c2.count)): - connections.append( - infra.ComponentConnection( - link=infra.ComponentLink( - c1=c1.name, - c1_index=c1_idx, - link=link.name, - c2=c2.name, - c2_index=c2_idx, - ) - ) - ) - self._device.connections.extend(connections) - - def get_nic_component(self) -> infra.Component: - for component in self._device.components.values(): - if component.HasField("nic") is True: - return component - raise Exception(f"Nic component does not exist in package {self._device.name}") - - def get_component( - self, component_type: COMPONENT_TYPES - ) -> Union[infra.Component, None]: - for component in self._device.components.values(): - if component.HasField(component_type) is True: - return component - return None - - def get_link(self, link_type: int) -> infra.Link: - for link in self._device.links: - if link.type == link_type: - return link - raise Exception( - f"Link type {link_type} does not exist in package {self._device.name}" - ) - - def get_adjacencies(self) -> List[str]: - adjacencies = [] - for adjacency in self._device.adjacencies: - adjacencies.append( - f"{adjacency.c1}.{adjacency.c1_index}.{adjacency.link}.{adjacency.c2}.{adjacency.c2_index}" - ) - return adjacencies - - def message_to_yaml(self, message) -> str: - return yaml.dump( - google.protobuf.json_format.MessageToDict( - message, - preserving_proto_field_name=True, - including_default_value_fields=True, - use_integers_for_enums=False, - ) - ) - - -class HostBuilder(DeviceBuilder): - def __init__(self): - super(DeviceBuilder).__init__() - - @property - @abstractmethod - def name(self) -> str: - pass - - @property - @abstractmethod - def description(self) -> str: - pass - - @property - @abstractmethod - def port_up_component(self) -> infra.Component: - pass - - -class FabricBuilder(ABC): - def __init__( - self, - name: str, - devices: List[Type[infra.Device]] = [], - links: List[Type[infra.Link]] = [], - ): - self._fabric = infra.CustomFabric( - name=name, - devices={}, - links={}, - connections=[], - ) - for package in devices: - self._fabric.devices[package.name].CopyFrom(package) - for link in links: - self._fabric.links[link.name].CopyFrom(link) - - @property - def fabric(self) -> infra.CustomFabric: - """Returns: infra_pb2.CustomFabric""" - return self._fabric - - @property - @abstractmethod - def name(self) -> str: - pass - - @property - @abstractmethod - def lowest_device(self) -> DeviceBuilder: - pass - - @property - @abstractmethod - def description(self) -> str: - pass - - def serialize(self, type: Literal["yaml", "json", "dict"]) -> Union[str, dict]: - """Returns the infra_pb2.Infrastructure as the specified serialization type""" - if type == "dict": - return google.protobuf.json_format.MessageToDict(self._fabric) - elif type == "yaml": - return yaml.dump(google.protobuf.json_format.MessageToDict(self._fabric)) - elif type == "json": - return google.protobuf.json_format.MessageToJson(self._fabric) - - def create_one_to_one_adjacencies( - self, - pb1: DeviceBuilder, - component1: int, - link: infra.Link, - pb2: DeviceBuilder, - component2: int, - mesh: bool = False, - ): - assert isinstance(pb1, DeviceBuilder) - assert isinstance(component1, infra.Component) - assert isinstance(link, infra.Link) - assert isinstance(pb2, DeviceBuilder) - assert isinstance(component2, infra.Component) - - indexes = Utilities.get_adjacency_indexes( - pb1.package, - component1, - pb2.package, - component2, - int(pb1.package.count * component1.count / pb2.package.count), - mesh, - ) - zipped_indexes = zip( - indexes["p1"]["indexes"], - indexes["c1"]["indexes"], - indexes["p2"]["indexes"], - indexes["c2"]["indexes"], - ) - adjacencies = [] - for p1, c1, p2, c2 in zipped_indexes: - adj = infra.DeviceConnection( - link=infra.DeviceLink( - p1=indexes["p1"]["name"], - p1_index=p1, - c1=indexes["c1"]["name"], - c1_index=c1, - link=link.name, - p2=indexes["p2"]["name"], - p2_index=p2, - c2=indexes["c2"]["name"], - c2_index=c2, - ) - ) - adjacencies.append(adj) - self._fabric.adjacencies.extend(adjacencies) - - def create_inter_package_adjacencies( - self, - package1: DeviceBuilder, - package1_indexes: int, - component1_indexes: Union[List[int], None], - package2: DeviceBuilder, - package2_indexes: int, - component2_indexes: Union[List[int], None], - link: infra.LinkType, - ): - """ - Builds inter-package adjacencies between two package components. - - Parameters - ---------- - p1: source package - component_type: source component type - c2_type: Destination component - link: Link between c1 and c2 - c1_indexes: The indexes of the c1 components that will be used when - creating adjacencies. If None then the entire count of the component - will be used. - """ - assert len(component1_indexes) == len(component2_indexes) - adjacencies = [] - c1 = package1.get_nic_component() - c2 = package2.get_nic_component() - for p1_index, p2_index in zip(package1_indexes, package2_indexes): - for c1_index, c2_index in zip(component1_indexes, component2_indexes): - adj = infra.DeviceConnection( - link=infra.DeviceConnection( - p1=package1.package.name, - p1_index=p1_index, - c1=c1.name, - c1_index=c1_index, - link=link.name, - p2=package2.package.name, - p2_index=p2_index, - c2=c2.name, - c2_index=c2_index, - ) - ) - adjacencies.append(adj) - self._fabric.adjacencies.extend(adjacencies) - - def create_many_to_many_inter_package_adjacencies( - self, - package1: DeviceBuilder, - package2: DeviceBuilder, - link: infra.LinkType, - index_pairs: List[Tuple[List[int], List[int]]], - ): - """ - Builds inter-package adjacencies between two package components. - - Need to be able to handle the following type of use case: - - pkg1[0].cmp1[0,1].link.pkg2[0].cmp2[0,1] - pkg1[0].cmp1[2,3].link.pkg2[1].cmp2[0,1] - pkg1[1].cmp1[0,1].link.pkg2[1].cmp2[2,3] - pkg1[1].cmp1[2,3].link.pkg2[0].cmp2[0,1] - - need something like this: - list[tuple(tuple(list[int], list[int]), tuple(list[int], list[int]))] - - Parameters - ---------- - src_dst_index_pairs: A list of src/dst index pairs. Each index pair - """ - adjacencies = [] - component1 = package1.get_nic_component() - component2 = package2.get_nic_component() - for src_idxs, dst_idxs in index_pairs: - for pkg1_idx, pkg2_idx in zip(src_idxs[0], dst_idxs[0]): - for cmp1_idx, cmp2_idx in zip(src_idxs[1], dst_idxs[1]): - adjacencies.append( - infra.DeviceConnection( - link=infra.DeviceLink( - p1=package1.package.name, - p1_index=pkg1_idx, - c1=component1.name, - c1_index=cmp1_idx, - link=link.name, - p2=package2.package.name, - p2_index=pkg2_idx, - c2=component2.name, - c2_index=cmp2_idx, - ) - ) - ) - self._fabric.adjacencies.extend(adjacencies) - - def get_link(self, link_type: int) -> infra.Link: - for link in self._fabric.links: - if link.type == link_type: - return link - raise Exception( - f"Inter package link of type {link_type} does not exist in system {self._fabric.name}" - ) - - -class InfraBuilder(ABC): - def __init__( - self, - fabric: infra.CustomFabric = infra.CustomFabric(), - hosts: infra.Hosts = infra.Hosts(), - connections: List[Type[infra.DeviceConnection]] = [], - links: List[Type[infra.Link]] = [], - ): - self._infra = infra.Infrastructure( - custom_fabric=fabric, hosts=hosts, connections=connections, links={} - ) - - for link in links: - self._infra.links[link.name].CopyFrom(link) - - @property - def infrastructure(self) -> infra.Infrastructure: - """Returns: infra_pb2.Infrastructure""" - return self._infra - - def serialize(self, type: Literal["yaml", "json", "dict"]) -> Union[str, dict]: - """Returns the infra_pb2.Infrastructure as the specified serialization type""" - if type == "dict": - return google.protobuf.json_format.MessageToDict(self._infra) - elif type == "yaml": - return yaml.dump(google.protobuf.json_format.MessageToDict(self._infra)) - elif type == "json": - return google.protobuf.json_format.MessageToJson(self._infra) diff --git a/src/closfabric.py b/src/closfabric.py deleted file mode 100644 index 5ebb42a..0000000 --- a/src/closfabric.py +++ /dev/null @@ -1,277 +0,0 @@ -""" Generic clos fabric switch and fabric -""" -from typing import List, Literal, Tuple, Union - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld -else: - from .generated import infra_pb2 as infra - from . import builders as bld - - -class ClosFabricSwitch(bld.DeviceBuilder): - name = "Generic fabric switch" - description = "Generic clos fabric switch" - - def __init__( - self, - name: str, - down_links: int, - up_links: int, - ): - """ - Creates a generic clos fabric switch device - - name: The name of the switch device - down_links: The number of ports used for down links - up_links: The number of ports used for up links - """ - super(ClosFabricSwitch).__init__() - asic = infra.Component( - name="asic", - count=1, - cpu=infra.Cpu(memory=infra.MemoryType.MEM_RAM), - ) - self._port_down = infra.Component( - name="port-down", - count=down_links, - nic=infra.Nic(ethernet=infra.Ethernet()), - ) - mac = infra.Link( - name="mac", - type=infra.LinkType.LINK_CUSTOM, - ) - self._device = infra.Device( - name=name, - components={ - asic.name: asic, - self._port_down.name: self._port_down, - }, - links={ - mac.name: mac, - }, - ) - self._create_distributed_intra_package_adjacencies( - asic, - mac, - self._port_down, - ) - self._port_up = None - if up_links > 0: - self._port_up = infra.Component( - name="port-up", - count=up_links, - nic=infra.Nic(ethernet=infra.Ethernet()), - ) - self.device.components[self._port_up.name].CopyFrom(self._port_up) - self._create_distributed_intra_package_adjacencies( - asic, - mac, - self._port_up, - ) - - @property - def port_down_component(self) -> infra.Component: - return self._port_down - - @property - def port_up_component(self) -> infra.Component: - return self._port_up - - -class ClosFabric(bld.FabricBuilder): - name: str = "Symmetric Clos Fabric" - description: str = "Symmetric clos fabric" - lowest_device: bld.DeviceBuilder = None - - def __init__( - self, - host_device: bld.DeviceBuilder, - host_devices: int = 1, - rack_capacity: int = 1, - rack_to_pod_oversubscription: Tuple[int, int] = (1, 1), - pod_capacity: int = 0, - pod_to_spine_oversubscription: Tuple[int, int] = (1, 1), - spine_capacity: int = 0, - ): - """Creates an infrastructure of host/rack/pod/spine devices. - - host_device: The host device to be used in the infrastructure - host_devices: The number of host devices to be represented in the infrastructure - rack_capacity: The number of host devices per rack switch - The following are derived from this parameter: - - the number of downlinks per rack switch - - the number of rack switches per pod - rack_to_pod_over_subscription: Tuple(downlink factor, uplink_factor). This parameter determines the number of - uplinks from rack to pod switches. # of uplinks = (# of downlinks * uplink_factor) / downlink_factor - pod_capacity: The number of rack switches per pod switch - The following are derived from this parameters: - - the number of pod switches - pod_to_spine_over_subscription: Tuple(downlink factor, uplink_factor) This parameter determines the number of - uplinks from pod to spine switches. # of uplinks = (# of downlinks * uplink_factor) / downlink_factor - spine_capacity: The number of pod switches per spine switch - The following are derived from this parameter: - - the number of spine switches - """ - super().__init__(name="clos fabric") - assert isinstance(host_device, bld.DeviceBuilder) - assert host_devices >= 1 - - self._rack_device, self._rack_devices = self._add_fabric_devices( - host_device, - host_devices, - "rack", - rack_capacity, - rack_to_pod_oversubscription, - pod_capacity, - ) - self.lowest_device = self._rack_device - self._pod_device, self._pod_devices = self._add_fabric_devices( - self._rack_device, - self._rack_devices, - "pod", - pod_capacity, - pod_to_spine_oversubscription, - spine_capacity, - ) - self._spine_device, self._spine_devices = self._add_fabric_devices( - self._pod_device, - self._pod_devices, - "spine", - spine_capacity, - ) - device_link = infra.Link( - name="eth", - type=infra.LinkType.LINK_ETHERNET, - ) - self.fabric.links[device_link.name].CopyFrom(device_link) - - # add a connection between rack_device nics and pod_device nics - if pod_capacity > 0: - self._add_connection( - device_link.name, - self._rack_device, - self._rack_devices, - pod_capacity, - self._pod_device, - self._pod_devices, - distribute_connections=True, - ) - - def _add_fabric_devices( - self, - lower_device, - lower_devices, - device_name, - lower_capacity, - over_subscription=(1, 1), - upper_capacity=None, - ) -> Tuple[bld.DeviceBuilder, int]: - """Adds fabric switches to the infrastructure - - Calculates the number of fabric switches and downlinks/uplinks for each - fabric switch. - - Returns: Tuple of the device and the number of devices - """ - if lower_capacity == 0: - return (None, None) - devices = int(lower_devices / lower_capacity) - down_links = int(lower_device.port_up_component.count * lower_capacity) - if upper_capacity == None or upper_capacity == 0: - up_links = 0 - elif over_subscription[0] > 0 and over_subscription[1] > 0: - up_links = (down_links * over_subscription[1]) // over_subscription[0] - else: - raise ValueError( - f"Unsupported over subscription value '{over_subscription}', values must be greater than zero" - ) - device = ClosFabricSwitch(device_name, down_links, up_links) - self._add_device(device, devices) - return (device, devices) - - def _add_device(self, package_builder: bld.DeviceBuilder, devices: int) -> None: - if package_builder is not None: - self.fabric.devices[package_builder.device.name].CopyFrom( - infra.DeviceCount( - count=devices, - device=package_builder.device, - ) - ) - - def _add_connection( - self, - link_name: str, - lower_device: bld.DeviceBuilder, - lower_number_of_devices: int, - lower_number_of_devices_per_upper_device: int, - upper_device: bld.DeviceBuilder, - upper_number_of_devices: int, - distribute_connections: bool = False, - ) -> None: - """Add a connection that is standard for a clos fabric - - If distribute_connections is false then all connections from one - device will go to one other device. - - If distribute_connections is true then all connections from the lower - device will be distributed evenly across all upper devices. - """ - connections = [] - if isinstance(lower_device, bld.DeviceBuilder) is True: - c1_component = lower_device.get_nic_component() - else: - c1_component = lower_device.port_up_component - c2_component = upper_device.port_down_component - - if distribute_connections is True and upper_number_of_devices > 1: - # create a generator for every upper_device component - def c2_index(): - while True: - for i in range(c2_component.count): - yield i - - def d2_index(): - while True: - for i in range(upper_number_of_devices): - yield i - - get_c2_index = [] - for i in range(upper_number_of_devices): - get_c2_index.append(c2_index()) - get_d2_index = d2_index() - else: - # one generator for all upper_device components - def c2_index(): - while True: - for i in range(c2_component.count): - yield i - - get_c2_index = c2_index() - - # create connections from lower devices (d1_idx) to upper devices (d2_idx) - for d1_idx in range(lower_number_of_devices): - # this is a one-to-one connection from d1_idx -> d2_idx - d2_idx = int(d1_idx / lower_number_of_devices_per_upper_device) - for c1_idx in range(c1_component.count): - if distribute_connections is True and upper_number_of_devices > 1: - # this is a distributed connection - # one connection from each d1_idx.c1_idx to every d2_idx.c2_idx (round robin) - d2_idx = next(get_d2_index) - c2_idx = next(get_c2_index[d2_idx]) - else: - c2_idx = next(get_c2_index) - device_link = infra.DeviceLink( - d1=lower_device.device.name, - d1_index=d1_idx, - c1=c1_component.name, - c1_index=c1_idx, - link=link_name, - d2=upper_device.device.name, - d2_index=d2_idx, - c2=c2_component.name, - c2_index=c2_idx, - ) - connections.append(infra.DeviceConnection(link=device_link)) - self.fabric.connections.extend(connections) diff --git a/src/generic.py b/src/generic.py deleted file mode 100644 index 75af49e..0000000 --- a/src/generic.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Generic host device -""" -from typing import List, Literal, Type, Tuple - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld -else: - from .generated import infra_pb2 as infra - from . import builders as bld - - -class GenericHost(bld.HostBuilder): - """Generic host device that has a one to one relationship between - npu and nic components. - """ - name : str = "Generic host" - description : str = "Generic host device consisting of an NPU and NIC component" - - def __init__( - self, - npu_count=1, - npu_interconnect_bandwidth_gbps: int=0 - ): - """Creates a generic device with only npu and nic components that are - connected by a pcie link. - - Optionally, npu components within a device can be interconnected via generic links attached to a single generic switch. - - name: The name of the generic device - npu_count: The number of npu/nic components in the device. - npu_interconnect_bandwidth_gbps: npu-to-npu interconnect bandwidth in gigabits per second. If 0, no internal npu-to-npu connectivity will be added to the device. - """ - super(GenericHost).__init__() - npu = infra.Component( - name="npu", - count=npu_count, - npu=infra.Npu(), - ) - self._port_component = infra.Component( - name="nic", - count=npu_count, - nic=infra.Nic(ethernet=infra.Ethernet()), - ) - pcie = infra.Link( - name="pcie", - type=infra.LinkType.LINK_PCIE, - ) - npu_interconnect = infra.Link( - name="npu_interconnect", - type=infra.LinkType.LINK_CUSTOM, - bandwidth=infra.Bandwidth(gbps=npu_interconnect_bandwidth_gbps), - ) - npu_interconnect_switch = infra.Component( - name="npu_interconnect_switch", - count=1, - switch=infra.Switch(custom=infra.Custom()), - ) - - links = { pcie.name: pcie } - components = { - npu.name: npu, - self._port_component.name: self._port_component, - } - connections = [] - # Add npu->nic pcie connections - for npu_idx in range(npu_count): - connections.append( - infra.ComponentConnection( - link=infra.ComponentLink( - c1=npu.name, - c1_index=npu_idx, - link=pcie.name, - c2=self._port_component.name, - c2_index=npu_idx, - ) - ) - ) - - # Add npu_interconnect connections if bandwidth was provided - if npu_interconnect_bandwidth_gbps > 0: - components[npu_interconnect_switch.name] = npu_interconnect_switch - links[npu_interconnect.name] = npu_interconnect - for npu_idx_a in range(npu_count): - connections.append( - infra.ComponentConnection( - link=infra.ComponentLink( - c1=npu.name, - c1_index=npu_idx_a, - link=npu_interconnect.name, - c2=npu_interconnect_switch.name, - c2_index=0, - ) - ) - ) - - self._device = infra.Device( - name=self.name, - components=components, - links=links, - connections=connections, - ) - - @property - def port_up_component(self) -> infra.Component: - return self._port_component - - @property - def port_down_component(self) -> infra.Component: - return None - diff --git a/src/infrastructure.py b/src/infrastructure.py deleted file mode 100644 index ae35e5c..0000000 --- a/src/infrastructure.py +++ /dev/null @@ -1,137 +0,0 @@ -""" Generic infrastructure -""" -from typing import List, Literal, Tuple, Union - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld -else: - from .generated import infra_pb2 as infra - from . import builders as bld - -class Infrastructure(bld.InfraBuilder): - name: str = "Generic Infrastructure" - description: str = "Generic Infrastructure" - - def __init__( - self, - host_device: bld.DeviceBuilder, - host_devices: int = 1, - fabric: bld.FabricBuilder = None, - assignment_scheme: Literal["NONE", "ROUND_ROBIN", "ONE_TO_ONE"] = "NONE" - ): - """Creates an infrastructure of host and fabric. - - parameters - ---------- - - host_device: The host device to be used in the infrastructure - - host_devices: The number of host devices to be represented in the infrastructure - - fabric: The fabric the hosts will connect to. If none, no host to fabric connections will be made - - assignment_scheme: the method used to generate the host to fabric connections. if NONE, no host to fabric connections will be made - - lowest_fabric_switch: the name of the device that the hosts will connect to - """ - assert isinstance(host_device, bld.DeviceBuilder) - assert host_devices >= 1 - if fabric: - super().__init__(fabric=fabric.fabric, hosts=infra.Hosts(devices=infra.DeviceCount(count=host_devices, device=host_device.device))) - else: - super().__init__(hosts=infra.Hosts(devices=infra.DeviceCount(count=host_devices, device=host_device.device))) - - if assignment_scheme != "NONE": - device_link = infra.Link( - name="eth", - type=infra.LinkType.LINK_ETHERNET, - ) - self.infrastructure.links[device_link.name].CopyFrom(device_link) - distribute_connections = (assignment_scheme == "ROUND_ROBIN") - self._add_connection( - link_name=device_link.name, - lower_device=host_device, - lower_number_of_devices=host_devices, - lower_number_of_devices_per_upper_device=fabric.lowest_device.port_down_component.count, - upper_device=fabric.lowest_device, - upper_number_of_devices=self.infrastructure.custom_fabric.devices[fabric.lowest_device.device.name].count, - distribute_connections=distribute_connections - ) - # get the port down components of the fabric and the port up components of the hosts - - # create the link - - #store link in self.infrastructure.connections - - def _add_connection( - self, - link_name: str, - lower_device: bld.DeviceBuilder, - lower_number_of_devices: int, - lower_number_of_devices_per_upper_device: int, - upper_device: bld.DeviceBuilder, - upper_number_of_devices: int, - distribute_connections = False, - ) -> None: - """Add a connections between hosts and fabric - - If distribute_connections is false then all connections from one - device will go to one other device. - - If distribute_connections is true then all connections from the lower - device will be distributed evenly across all upper devices. - """ - connections = [] - if isinstance(lower_device, bld.DeviceBuilder) is True: - c1_component = lower_device.get_nic_component() - else: - c1_component = lower_device.port_up_component - c2_component = upper_device.port_down_component - - if distribute_connections is True and upper_number_of_devices > 1: - # create a generator for every upper_device component - def c2_index(): - while True: - for i in range(c2_component.count): - yield i - - def d2_index(): - while True: - for i in range(upper_number_of_devices): - yield i - - get_c2_index = [] - for i in range(upper_number_of_devices): - get_c2_index.append(c2_index()) - get_d2_index = d2_index() - else: - # one generator for all upper_device components - def c2_index(): - while True: - for i in range(c2_component.count): - yield i - - get_c2_index = c2_index() - - # create connections from lower devices (d1_idx) to upper devices (d2_idx) - for d1_idx in range(lower_number_of_devices): - # this is a one-to-one connection from d1_idx -> d2_idx - # divides hosts evenly between all switches, alternating which switch it is applying a host to - d2_idx = int(d1_idx / lower_number_of_devices_per_upper_device) - for c1_idx in range(c1_component.count): - if distribute_connections is True and upper_number_of_devices > 1: - # this is a distributed connection - # one connection from each d1_idx.c1_idx to every d2_idx.c2_idx (round robin) - d2_idx = next(get_d2_index) - c2_idx = next(get_c2_index[d2_idx]) - else: - c2_idx = next(get_c2_index) - device_link = infra.DeviceLink( - d1=lower_device.device.name, - d1_index=d1_idx, - c1=c1_component.name, - c1_index=c1_idx, - link=link_name, - d2=upper_device.device.name, - d2_index=d2_idx, - c2=c2_component.name, - c2_index=c2_idx, - ) - connections.append(infra.DeviceConnection(link=device_link)) - self.infrastructure.connections.extend(connections) \ No newline at end of file diff --git a/src/notes/infrastructure.ipynb b/src/notes/infrastructure.ipynb deleted file mode 100644 index 68f4515..0000000 --- a/src/notes/infrastructure.ipynb +++ /dev/null @@ -1,281 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Overview\n", - "\n", - "This file describes how to create a system as a graph.\n", - "\n", - "This system is comprised of the following:\n", - "\n", - "- a ZionEx host package\n", - "- a generic rack switch package\n", - "- a generic pod switch package\n", - "- a system that connects those packages in a clos fabric\n", - "- two ZionEx packages connected to 2 rack switches\n", - "- two rack switches connected to 2 pod switches\n", - "\n", - "It also includes a sample use case that describes how host component paths in\n", - "the ZionEx host can be used to create collective communication optimizations.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Define a ZionEx Package\n", - "\n", - "This ZionEx class demonstrates how to take the ZionEx block diagram and capture the following:\n", - "\n", - "- the package as a collection of components, links and adjacencies between them using `sys.proto`\n", - "\n", - "![ZionEx Block Diagram](./meta-zion-ex.png)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create the ZionEx Device\n", - "\n", - "- Create an instance of the ZionEx device\n", - "- Check the ZionEx device by creating a d3graph visualization\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'et_def_pb2'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[1], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01msys\u001b[39;00m\n\u001b[1;32m 3\u001b[0m sys\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m..\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mzionex\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m ZionEx\n\u001b[1;32m 6\u001b[0m zionex \u001b[38;5;241m=\u001b[39m ZionEx()\n\u001b[1;32m 7\u001b[0m zionex\u001b[38;5;241m.\u001b[39mvisualize()\u001b[38;5;241m.\u001b[39mjupyter_display()\n", - "File \u001b[0;32m~/github/ajbalogh/chakra_system/src/notes/../zionex.py:10\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtyping\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m List, Literal, Type, Union\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m __package__ \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m __package__ \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mgenerated\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01minfra_pb2\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01minfra\u001b[39;00m\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mbuilders\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mbld\u001b[39;00m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/github/ajbalogh/chakra_system/src/notes/../generated/infra_pb2.py:14\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;66;03m# @@protoc_insertion_point(imports)\u001b[39;00m\n\u001b[1;32m 11\u001b[0m _sym_db \u001b[38;5;241m=\u001b[39m _symbol_database\u001b[38;5;241m.\u001b[39mDefault()\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01met_def_pb2\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01met__def__pb2\u001b[39;00m\n\u001b[1;32m 17\u001b[0m DESCRIPTOR \u001b[38;5;241m=\u001b[39m _descriptor_pool\u001b[38;5;241m.\u001b[39mDefault()\u001b[38;5;241m.\u001b[39mAddSerializedFile(\u001b[38;5;124mb\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124minfra.proto\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124minfra\u001b[39m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mt_def.proto\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m4\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;124mustomComponent\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m!\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mmemory\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.MemoryType\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mNpu\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m!\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mmemory\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.MemoryType\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;124mpu\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m!\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mmemory\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.MemoryType\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mInfiniband\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x45\u001b[39;00m\u001b[38;5;124mthernet\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mZ\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mNic\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m#\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mthernet\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;124m.infra.EthernetH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m&\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124minfinband\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.InfinibandH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mPcie\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mNvLink\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mN\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mSwitch\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mpcie\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m.infra.PcieH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mnvlink\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124m.infra.NvLinkH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\x80\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mComponent\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mname\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124mount\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124mustom\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x16\u001b[39;00m\u001b[38;5;124m.infra.CustomComponentH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x19\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124mpu\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m.infra.CpuH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x19\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mnpu\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m.infra.NpuH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x19\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mnic\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m.infra.NicH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mswitch\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124m.infra.SwitchH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mattributes\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1e\u001b[39;00m\u001b[38;5;124m.ChakraProtoMsg.AttributeProtoB\u001b[39m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mA\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mBandwidth\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mgbps\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mgBs\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mgts\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m;\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mLatency\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124mms\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124mus\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124mns\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\xad\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mLink\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mname\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1d\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;124m.infra.LinkType\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m#\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mbandwidth\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;124m.infra.Bandwidth\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mlatency\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;124m.infra.Latency\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mattributes\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1e\u001b[39;00m\u001b[38;5;124m.ChakraProtoMsg.AttributeProto\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mY\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mComponentLink\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mlink\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mU\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x13\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;124momponentConnection\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m$\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mlink\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x14\u001b[39;00m\u001b[38;5;124m.infra.ComponentLinkH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mstring\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\xa3\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\x44\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mvice\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mname\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mcomponents\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1d\u001b[39;00m\u001b[38;5;124m.infra.Device.ComponentsEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\'\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mlinks\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;124m.infra.Device.LinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124monnections\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;124m.infra.ComponentConnection\u001b[39m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;124momponentsEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;124m.infra.Component:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x39\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mLinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m.infra.Link:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\x92\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mDeviceLink\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mlink\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;124m_index\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124mO\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x44\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mviceConnection\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m!\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mlink\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.DeviceLinkH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mstring\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m;\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x44\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mviceCount\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124mount\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1d\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mvice\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124m.infra.Device\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\xab\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\x43\u001b[39;00m\u001b[38;5;124mustomFabric\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124mname\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x31\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mvices\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;124m .infra.CustomFabric.DevicesEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m-\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mlinks\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1e\u001b[39;00m\u001b[38;5;124m.infra.CustomFabric.LinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124monnections\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x17\u001b[39;00m\u001b[38;5;124m.infra.DeviceConnection\u001b[39m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\x44\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mvicesEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m!\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m.infra.DeviceCount:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x39\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mLinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m.infra.Link:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\xa3\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mClosFabric\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mrack_switches\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m.infra.DeviceCount\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;124mpod_switches\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m.infra.DeviceCount\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m*\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;124mspine_switches\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m.infra.DeviceCount\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m+\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mlinks\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x1c\u001b[39;00m\u001b[38;5;124m.infra.ClosFabric.LinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124monnections\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x17\u001b[39;00m\u001b[38;5;124m.infra.DeviceConnection\u001b[39m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x39\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mLinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m.infra.Link:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mHosts\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m#\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;130;01m\\x64\u001b[39;00m\u001b[38;5;130;01m\\x65\u001b[39;00m\u001b[38;5;124mvices\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m.infra.DeviceCount\u001b[39m\u001b[38;5;130;01m\\\"\u001b[39;00m\u001b[38;5;130;01m\\xad\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0e\u001b[39;00m\u001b[38;5;124mInfrastructure\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mhosts\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;124m.infra.Hosts\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mcustom_fabric\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x13\u001b[39;00m\u001b[38;5;124m.infra.CustomFabricH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124mlos_fabric\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;124m.infra.ClosFabricH\u001b[39m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mlinks\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;124m .infra.Infrastructure.LinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;124m,\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x63\u001b[39;00m\u001b[38;5;124monnections\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x17\u001b[39;00m\u001b[38;5;124m.infra.DeviceConnection\u001b[39m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\x39\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mLinksEntry\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124mkey\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x1a\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;124mvalue\u001b[39m\u001b[38;5;130;01m\\x18\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;124m(\u001b[39m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\x32\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124m.infra.Link:\u001b[39m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x38\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x42\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mfabricType*H\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mMemoryType\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x13\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;124mMEM_UNSPECIFIED\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mMEM_RAM\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mMEM_HBM\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;124mMEM_CXL\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;124m*\u001b[39m\u001b[38;5;130;01m\\x95\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;124mLinkType\u001b[39m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x14\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;124mLINK_UNSPECIFIED\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x00\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124mLINK_CUSTOM\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x01\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x11\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;124mLINK_ETHERNET\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x02\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\r\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\t\u001b[39;00m\u001b[38;5;124mLINK_PCIE\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x03\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0b\u001b[39;00m\u001b[38;5;124mLINK_NVLINK\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x04\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;124mLINK_QPI\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x05\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x0c\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x08\u001b[39;00m\u001b[38;5;124mLINK_UPI\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;130;01m\\x12\u001b[39;00m\u001b[38;5;130;01m\\x13\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\x0f\u001b[39;00m\u001b[38;5;124mLINK_INFINIBAND\u001b[39m\u001b[38;5;130;01m\\x10\u001b[39;00m\u001b[38;5;130;01m\\x07\u001b[39;00m\u001b[38;5;130;01m\\x62\u001b[39;00m\u001b[38;5;130;01m\\x06\u001b[39;00m\u001b[38;5;124mproto3\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 19\u001b[0m _globals \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mglobals\u001b[39m()\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'et_def_pb2'" - ] - } - ], - "source": [ - "import sys\n", - "\n", - "sys.path.append(\"..\")\n", - "sys.path.append(\"../generated\")\n", - "from zionex import ZionEx\n", - "\n", - "zionex = ZionEx()\n", - "zionex.visualize().jupyter_display()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Define a generic Switch package\n", - "\n", - "Create a class that models a generic switch which can be used in a fabric.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"Generic switch device\n", - "\n", - "This can be used to create devices to be used in the formation of the\n", - "infrastructure fabric.\n", - "\"\"\"\n", - "\n", - "from typing import Type\n", - "import infra_pb2\n", - "import builders\n", - "\n", - "\n", - "class Switch(builders.PackageBuilder):\n", - " def __init__(self, name: str, down_links: int, up_links: int = 0):\n", - " \"\"\"\n", - " Builds a generic switch device.\n", - "\n", - " Parameters\n", - " ----------\n", - " name: The name of the switch device\n", - " down_links: The number of ports used for down link adjacencies\n", - " up_links: The number of ports used for up link adjacencies\n", - " \"\"\"\n", - " super().__init__()\n", - " asic = infra_pb2.Component(\n", - " name=\"asic\",\n", - " count=1,\n", - " cpu=infra_pb2.Cpu(memory=infra_pb2.MemoryType.MEM_RAM),\n", - " )\n", - " self._port_down = infra_pb2.Component(\n", - " name=\"port-down\",\n", - " count=down_links,\n", - " nic=infra_pb2.Nic(ethernet=infra_pb2.Ethernet()),\n", - " )\n", - " mac = infra_pb2.Link(\n", - " name=\"mac\",\n", - " type=infra_pb2.LinkType.LINK_CUSTOM,\n", - " )\n", - " self._device = infra_pb2.Device(\n", - " name=name,\n", - " components={\n", - " asic.name: asic,\n", - " self._port_down.name: self._port_down,\n", - " },\n", - " links={\n", - " mac.name: mac,\n", - " },\n", - " )\n", - " self._create_distributed_intra_package_adjacencies(\n", - " asic,\n", - " mac,\n", - " self._port_down,\n", - " )\n", - " self._port_up = None\n", - " if up_links > 0:\n", - " self._port_up = infra_pb2.Component(\n", - " name=\"port-up\",\n", - " count=up_links,\n", - " nic=infra_pb2.Nic(ethernet=infra_pb2.Ethernet()),\n", - " )\n", - " self._device.components[self._port_up.name].CopyFrom(self._port_up)\n", - " self._create_distributed_intra_package_adjacencies(\n", - " asic,\n", - " mac,\n", - " self._port_up,\n", - " )\n", - "\n", - " @property\n", - " def port_down_component(self) -> Type[infra_pb2.Component]:\n", - " return self._port_down\n", - "\n", - " @property\n", - " def port_up_component(self) -> Type[infra_pb2.Component]:\n", - " return self._port_up" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create the Fabric Switch packages\n", - "\n", - "- an instance of a rack switch with 8 down link ports and 8 up link ports\n", - "- an instance of a pod switch with 8 down link ports\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rack_switch = Switch(\"rack\", down_links=8, up_links=8)\n", - "pod_switch = Switch(\"pod\", down_links=8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create the System\n", - "\n", - "The system should consist of the following:\n", - "\n", - "- 2 ZionEx packages\n", - "- 2 rack switch packages\n", - "- 2 pod switch packages\n", - "- adjacencies between ZionEx packages and rack switch packages\n", - "- adjacencies between rack switch packages and pod switch packages\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# use the helper ClosFabric class which will create a fabric and\n", - "# establish connections between the device instances\n", - "fabric = builders.ClosFabric(\n", - " host_devices=infra_pb2.DeviceCount(count=2, device=zionex._device),\n", - " rack_devices=infra_pb2.DeviceCount(count=2, device=rack_switch),\n", - " pod_devices=infra_pb2.DeviceCount(count=2, device=pod_switch),\n", - ")\n", - "\n", - "# visualize the entire system\n", - "fabric.visualize().jupyter_display()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Communication Collective Optimization Use Case\n", - "\n", - "Use Case:\n", - "\n", - "> Communication between ranks MUST use internal paths if available\n", - "> and external paths if no internal path is available.\n", - "\n", - "Example:\n", - "\n", - "> 16 ranks over 2 ZionEx packages (1 rank per oam)\n", - "\n", - "- for `rank0 to rank6` (ranks are in one ZionEx host)\n", - " - determine the source component for rank0 and destination component for rank6\n", - " - evaluate algorithms in order\n", - " - the first algorithm `internal` yields a path `oam.0,oam-link,ep-oam-sw.0,oam-link,oam.7`\n", - "- for `rank1 to rank10` (ranks are in different ZionEx hosts)\n", - " - determine the source component for rank1 and destination component for rank10\n", - " - evaluate algorithms in order\n", - " - the first algorithm `internal` yields no match\n", - " - move to the next algorithm `external` which yields a path `oam.1,pcie4,ep-pcie-sw.0,pcie4,cc-pcie-sw.0,pcie3,nic.1`\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create a Collective Optimization\n", - "\n", - "The following creates a communication collective optimization for the `all to all` communication collective that consists of the following:\n", - "\n", - "- an `internal` algorithm for communication between oam components in the same package\n", - "- an `external` algorithm for communication between oam components in different packages\n", - "- a `LIFO` schedule\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/src/notes/meta-zion-ex.png b/src/notes/meta-zion-ex.png deleted file mode 100644 index c02cf5f..0000000 Binary files a/src/notes/meta-zion-ex.png and /dev/null differ diff --git a/src/notes/rack-plane.png b/src/notes/rack-plane.png deleted file mode 100644 index 50c172b..0000000 Binary files a/src/notes/rack-plane.png and /dev/null differ diff --git a/src/rack_plane_fabric.py b/src/rack_plane_fabric.py deleted file mode 100644 index 6957e85..0000000 --- a/src/rack_plane_fabric.py +++ /dev/null @@ -1,73 +0,0 @@ -"""RackPlaneFabric package - -Uses the infra_pb2 protobuf generated code -to capture components, links, and connections of the -RackPlane block diagram. -""" - -from typing import Tuple - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld - from keysight_chakra.closfabric import ClosFabricSwitch -else: - from .generated import infra_pb2 as infra - from . import builders as bld - from .closfabric import ClosFabricSwitch - - -class RackPlaneFabricBuilder(bld.FabricBuilder): - """ - generates infrastructure of a fabric that - supports connecting to switching via multiple planes - """ - - name: str = "RackPlaneFabric" - description: str = "fabric that users multiple planes inside a rack" - lowest_device: bld.DeviceBuilder = None - - def __init__(self, host_builder: bld.DeviceBuilder, host_count: int = 1): - super().__init__(self.name) - assert isinstance(host_builder, bld.DeviceBuilder) - - rack_switch, _ = self._add_fabric_devices( - host_builder, - host_count, - "RackSwitch", - ) - self.lowest_device = rack_switch - - device_link = infra.Link( - name="eth", - type=infra.LinkType.LINK_ETHERNET, - ) - self.fabric.links[device_link.name].CopyFrom(device_link) - - def _add_fabric_devices( - self, - host_builder: bld.DeviceBuilder, - host_count: int, - device_name: str, - ) -> Tuple[bld.DeviceBuilder, int]: - """Adds fabric switches to the infrastructure - Returns: Tuple of the device and the number of devices - """ - down_link_count = int(host_builder.port_up_component.count * host_count) - up_link_count = 0 - device = ClosFabricSwitch(device_name, down_link_count, up_link_count) - # create one rack switch per host scale up nic - sw_count = host_builder.port_up_component.count - self._add_device(device, sw_count) - return (device, sw_count) - - def _add_device( - self, package_builder: bld.DeviceBuilder, device_count: int - ) -> None: - if package_builder is not None: - self.fabric.devices[package_builder.device.name].CopyFrom( - infra.DeviceCount( - count=device_count, - device=package_builder.device, - ) - ) diff --git a/src/rack_plane_host.py b/src/rack_plane_host.py deleted file mode 100644 index bb63a49..0000000 --- a/src/rack_plane_host.py +++ /dev/null @@ -1,81 +0,0 @@ -"""RackPlaneHost package - -Uses the infra_pb2 protobuf generated code -to capture components, links, and connections of the -RackPlane block diagram. -""" - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld -else: - from .generated import infra_pb2 as infra - from . import builders as bld - - -class RackPlaneHostBuilder(bld.HostBuilder): - """ - generates infrastructure of a host that - supports connecting to switching via multiple planes - """ - - name = "RackPlaneHost" - description = "a host with dedicated scale up and scale out NICs" - - def __init__( - self, npu_count: int, scale_up_nic_count: int, scale_out_nic_count: int - ): - super(RackPlaneHostBuilder).__init__() - # 1. Add components - npu = infra.Component(name="npu", count=npu_count, npu=infra.Npu()) - scale_up_nic = infra.Component( - name="scale-up-nic", count=scale_up_nic_count, nic=infra.Nic() - ) - self._port_component = scale_up_nic - - # TODO: Scale OUT NICs - # scale_out_nic = infra.Component( - # name="scale-out-nic", count=scale_out_nic_count, nic=infra.Nic() - # ) - - # 2. Add link & device - # link is yet undetermined, using mii as placeholder with zero cost speed - mii_link = infra.Link(name="mii") - - self._device = infra.Device( - name=self.name, - components={ - npu.name: npu, - scale_up_nic.name: scale_up_nic, - # scale_out_nic.name: scale_out_nic, - }, - links={mii_link.name: mii_link}, - ) - - # 3. Add component links - # scale UP NICs to NPU connections - for c1_index in range(npu.count): - for c2_index in range(scale_up_nic.count): - self._add_component_link( - npu.name, - c1_index, - mii_link.name, - scale_up_nic.name, - c2_index, - ) - - # TODO: Scale OUT NICs - # scale OUT NICs to NPU connections - # for c1_index in range(npu.count): - # for c2_index in range(scale_up_nic.count): - # self._add_component_link( - # npu.name, - # c1_index, - # mii_link.name, - # scale_up_nic.name, - # c2_index, - # ) - - @property - def port_up_component(self) -> infra.Component: - return self._port_component diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 5871ed8..3fd86b7 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -1 +1,28 @@ +import sys +import os + +for directory_piece in ["..", "generated", "tests"]: + sys.path.append(os.path.join(os.path.dirname(__file__), directory_piece)) + import pytest +from keysight_chakra.generated.infra_pb2 import Device, Component, Link, Bandwidth, Npu, Nic +from validation import Validation + + +@pytest.fixture +def validation() -> Validation: + return Validation() + + +@pytest.fixture +def host() -> Device: + device = Device( + name="aic-sb203-lx", + components={ + "tesla-t4": Component(name="tesla-t4", count=1, npu=Npu()), + "cx6": Component(name="cx6", count=1, nic=Nic()), + }, + links={"pcie-3": Link(name="pcie-3", bandwidth=Bandwidth(gbps=32))}, + connections=["tesla-t4.0.pcie-3.cx6.0"], + ) + return device diff --git a/src/tests/test_device.py b/src/tests/test_device.py deleted file mode 100644 index f4ce2a1..0000000 --- a/src/tests/test_device.py +++ /dev/null @@ -1,83 +0,0 @@ -import pytest - -from keysight_chakra.generic import GenericHost -from keysight_chakra.closfabric import ClosFabric -from keysight_chakra.zionex import ZionEx -from keysight_chakra.infrastructure import Infrastructure - -import sys -import os -sys.path.append(os.path.dirname(os.path.realpath(__file__))+"/../") -import generated.infra_pb2 as infra - - -def test_generic_host_no_params(): - host = GenericHost() - assert host.get_component("npu") is not None - assert host.get_component("nic") is not None - assert "npu_interconnect" not in host._device.links - -def test_generic_host_with_params(): - npu_count = 4 - host = GenericHost(npu_count=npu_count, npu_interconnect_bandwidth_gbps=600) - assert "npu_interconnect" in host._device.links - assert host._device.links["npu_interconnect"].type == infra.LINK_CUSTOM - - seen_map = {} - for npu_index in range(npu_count): - seen_map[npu_index] = False - - for connection in host._device.connections: - if connection.link.c1 == "npu" and connection.link.c2 == "npu_interconnect_switch": - npu_index = connection.link.c1_index - assert npu_index in seen_map - assert not seen_map[npu_index] - seen_map[npu_index] = True - for v in seen_map.values(): - assert v == True - -@pytest.mark.parametrize( - "host_devices, rack_capacity, pod_capacity, spine_capacity", - [ - (8, 4, 1, 0), - (4, 4, 0, 0), - ], -) -@pytest.mark.parametrize( - "over_subscription", - [(1, 1), (2, 1)], -) -def test_clos_fabric( - host_devices, - rack_capacity, - pod_capacity, - over_subscription, - spine_capacity, -): - """Test creating 2 tier clos fabric""" - host = GenericHost(npu_count=1) - clos_fabric = ClosFabric( - host_device=host, - host_devices=host_devices, - rack_capacity=rack_capacity, - rack_to_pod_oversubscription=over_subscription, - pod_capacity=pod_capacity, - spine_capacity=spine_capacity, - ) - infrastructure = Infrastructure( - host_device=host, - host_devices=host_devices, - fabric=clos_fabric, - assignment_scheme="ROUND_ROBIN", - ) - - -def test_zionex_host(): - host = ZionEx() - assert host.get_component("cpu") is not None - assert host.get_component("npu") is not None - assert host.get_component("nic") is not None - - -if __name__ == "__main__": - pytest.main(["-s", __file__]) diff --git a/src/tests/test_rack_plane.py b/src/tests/test_rack_plane.py deleted file mode 100644 index 1ce34f8..0000000 --- a/src/tests/test_rack_plane.py +++ /dev/null @@ -1,81 +0,0 @@ -"""rack plane related unit tests""" - -import pytest - -if __package__ is None or __package__ == "": - from src.generated import infra_pb2 - from src.rack_plane_host import RackPlaneHostBuilder - from src.rack_plane_fabric import RackPlaneFabricBuilder - from src.infrastructure import Infrastructure -else: - from .generated import infra_pb2 - from keysight_chakra.rack_plane_host import RackPlaneHostBuilder - from keysight_chakra.rack_plane_fabric import RackPlaneFabricBuilder - from keysight_chakra.infrastructure import Infrastructure - - -@pytest.mark.parametrize("host_count", [2, 3, 4, 8]) -@pytest.mark.parametrize("sup_nic_count", [2, 3, 4]) -def test_rack_plane_fabric_and_host(host_count: int, sup_nic_count: int): - """verifies that the correct infrastructure can be created from rack_plane fabric/host""" - rp_host_builder = RackPlaneHostBuilder( - npu_count=1, scale_up_nic_count=sup_nic_count, scale_out_nic_count=1 - ) - rp_fabric_builder = RackPlaneFabricBuilder(host_builder=rp_host_builder) - infra_builder = Infrastructure( - host_device=rp_host_builder, - host_devices=host_count, - fabric=rp_fabric_builder, - assignment_scheme="ROUND_ROBIN", - ) - infrastructure = infra_builder.infrastructure - - assert infrastructure is not None - # loose check confirming the correct number of connections - # between host and rack switches - assert len(infrastructure.connections) == host_count * sup_nic_count - - -def test_rack_plane_fabric_and_host_detailed(): - """verifies that the correct infrastructure can be created from rack_plane fabric/host""" - sup_nic_count = 2 - host_count = 2 - rp_host_builder = RackPlaneHostBuilder( - npu_count=1, scale_up_nic_count=sup_nic_count, scale_out_nic_count=1 - ) - rp_fabric_builder = RackPlaneFabricBuilder(host_builder=rp_host_builder) - infra_builder = Infrastructure( - host_device=rp_host_builder, - host_devices=host_count, - fabric=rp_fabric_builder, - assignment_scheme="ROUND_ROBIN", - ) - infrastructure = infra_builder.infrastructure - - assert infrastructure is not None - assert len(infrastructure.connections) == host_count * sup_nic_count - - # now let's confirm every details of the DeviceConnections - def assert_device_conn( - dev_conn: infra_pb2.DeviceConnection, - d1_index: int, - c1_index: int, - d2_index: int, - c2_index: int, - ): - assert dev_conn.link.d1 == "RackPlaneHost" - assert dev_conn.link.c1 == "scale-up-nic" - assert dev_conn.link.d2 == "RackSwitch" - assert dev_conn.link.c2 == "port-down" - assert dev_conn.link.link == "eth" - assert dev_conn.link.d1_index == d1_index - assert dev_conn.link.c1_index == c1_index - assert dev_conn.link.d2_index == d2_index - assert dev_conn.link.c2_index == c2_index - - # plane 0 d1,c1,d2,c2 - assert_device_conn(infrastructure.connections[0], 0, 0, 0, 0) - assert_device_conn(infrastructure.connections[1], 0, 1, 1, 0) - # plane 1 (and thus rack switch 1; and scale up nic 1 on all hosts) - assert_device_conn(infrastructure.connections[2], 1, 0, 0, 1) - assert_device_conn(infrastructure.connections[3], 1, 1, 1, 1) diff --git a/src/tests/test_validation.py b/src/tests/test_validation.py new file mode 100644 index 0000000..e377d73 --- /dev/null +++ b/src/tests/test_validation.py @@ -0,0 +1,70 @@ +import pytest + +from keysight_chakra.generated.service_pb2 import ValidationRequest +from keysight_chakra.generated.infra_pb2 import ( + Infrastructure, + Inventory, + Device, + Link, + Bandwidth, + Component, + DeviceInstances, +) + + +def test_validate_device(validation, host): + """Test that a device is valid""" + response = validation.validate( + request=ValidationRequest( + infrastructure=Infrastructure( + inventory=Inventory( + devices={ + host.name: host, + }, + ), + ) + ) + ) + assert len(response.errors) == 0 + + +def test_missing_bandwidth(validation): + """Test that a device is missing the bandwidth from a link""" + device = Device(name="host") + mii = Link(name="mii") + device.links[mii.name].CopyFrom(mii) + inventory = Inventory() + inventory.devices[device.name].CopyFrom(device) + infrastructure = Infrastructure(inventory=inventory) + request = ValidationRequest(infrastructure=infrastructure) + response = validation.validate(request=request) + print(response) + assert len(response.errors) == 1 + assert response.errors[0].WhichOneof("type") == "oneof" + + +def test_referential_integrity(validation): + """Referential integrity tests""" + device = Device(name="laptop") + mii = Link(name="mii", bandwidth=Bandwidth(gbps=100)) + device.links[mii.name].CopyFrom(mii) + asic = Component(name="asic", count=1) + nic = Component(name="nic", count=1) + device.components[asic.name].CopyFrom(asic) + device.components[nic.name].CopyFrom(nic) + device.connections.append(f"bad.device.component.connection") + device.connections.append(f"{asic.name}.x.{mii.name}.null.-1") + inventory = Inventory() + inventory.devices[device.name].CopyFrom(device) + infrastructure = Infrastructure(inventory=inventory) + host = DeviceInstances(name="host", device="laptop", count=4) + infrastructure.device_instances[host.name].CopyFrom(host) + request = ValidationRequest(infrastructure=infrastructure) + response = validation.validate(request=request) + print(response) + for error in response.errors: + assert error.WhichOneof("type") == "referential_integrity" + + +if __name__ == "__main__": + pytest.main(["-s", "-o", "log_cli=True", "-o", "log_cli_level=INFO", __file__]) diff --git a/src/utilities.py b/src/utilities.py deleted file mode 100644 index 07b0149..0000000 --- a/src/utilities.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Container class for various infrastructure utility methods -""" -from typing import List, Tuple - -class Utilities: - def get_indexes(c1, c2) -> List[Tuple[str, List[int]]]: - max = c1 if c1.count >= c2.count else c2 - min = c2 if max.name == c1.name else c1 - step = int(max.count / min.count) - min_idxs = [] - for idx in range(min.count): - min_idxs.extend([idx] * step) - return [[max.name, range(max.count)], [min.name, min_idxs]] - - def repeat_package(package, component, max_count): - """Returns a list""" - indexes = [] - while len(indexes) < max_count * package: - for i in range(package): - indexes.extend(itertools.repeat(i, min(max_count, component))) - return indexes - - def list_package(package, component, max_count): - """Returns a list""" - indexes = [] - while len(indexes) < max_count * package: - for i in range(package): - indexes.extend(itertools.repeat(i, package)) - indexes.extend(reversed(indexes)) - return indexes - - def cycle_component(component, max_count): - """Returns a list""" - return itertools.cycle(range(0, min(max_count, component))) - - def get_adjacency_indexes(p1, c1, p2, c2, max_count, mesh) -> dict: - """Returns a dict of param to name and indexes - ie "p1": {name: "p1", indexes: []} - """ - indexes = { - "p1": {"name": p1.name, "indexes": None}, - "c1": {"name": c1.name, "indexes": None}, - "p2": {"name": p2.name, "indexes": None}, - "c2": {"name": c2.name, "indexes": None}, - } - indexes["p1"]["indexes"] = Utilities.repeat_package( - p1.count, c1.count, max_count - ) - indexes["c1"]["indexes"] = Utilities.cycle_component(c1.count, max_count) - if mesh is True: - indexes["p2"]["indexes"] = Utilities.list_package( - p2.count, c2.count, max_count - ) - else: - indexes["p2"]["indexes"] = Utilities.repeat_package( - p2.count, c2.count, max_count - ) - indexes["c2"]["indexes"] = Utilities.cycle_component(c2.count, max_count) - return indexes diff --git a/src/validation.py b/src/validation.py new file mode 100644 index 0000000..cf00714 --- /dev/null +++ b/src/validation.py @@ -0,0 +1,154 @@ +"""Service class that can be used without the need for a grpc service and grpc +client. + +""" + +from typing import Annotated +from google.protobuf.message import Message + +if __package__ is None or __package__ == "": + from generated.service_pb2 import ValidationRequest, ValidationError, ValidationResponse + from generated.infra_pb2 import Device + from generated.bind_pb2 import Binding +else: + from .generated.service_pb2 import ValidationRequest, ValidationError, ValidationResponse + from .generated.infra_pb2 import Device + from .generated.bind_pb2 import Binding + + +class Validation: + def __init__(self): + self._validation_request = None + self._validation_response = None + + def _validate_presence(self, object: Message, name: str): + if object.HasField(name) is False: + self._validation_response.errors.append( + ValidationError(optional=f"{object.DESCRIPTOR.name} {name} field has not been set") + ) + + def _validate_map(self, map: Annotated[object, "google protobuf MessageMapContainer"]): + for key, object in map.items(): + if object.name != key: + self._validation_response.errors.append( + ValidationError( + map=f"{object.DESCRIPTOR.name} name value:{object.name} does not match map key:{key}'" + ) + ) + + def _validate_component_connection(self, device: Device, connection: str): + try: + c1, c1_idx, link, c2, c2_idx = connection.split(".") + self._validate_component(device, c1, c1_idx) + self._validate_component(device, c2, c2_idx) + self._validate_link_name(device, link) + except ValueError: + self._validation_response.errors.append( + ValidationError( + referential_integrity=f"Infrastructure.devices[{device.name}].connections:[{connection}] is incorrectly formatted" + ) + ) + + def _validate_component(self, device: Device, name: str, index: int): + if name not in device.components: + self._validation_response.errors.append( + ValidationError( + referential_integrity=f"Infrastructure.devices[{device.name}].components[{name}] does not exist" + ) + ) + try: + index = int(index) + if index < 0 or index > device.components[name].count - 1: + self._validation_response.errors.append( + ValidationError( + referential_integrity=f"Component:{name} index:{index} must be >= 0 and <{device.components[name].count}" + ) + ) + except ValueError: + self._validation_response.errors.append( + ValidationError(referential_integrity=f"Index:{index} must be a valid integer") + ) + + def _validate_link_name(self, device: Device, name: str): + if name not in device.links: + self._validation_response.errors.append( + ValidationError( + referential_integrity=f"Infrastructure.devices[{device.name}].links[{name}] does not exist" + ) + ) + + def _validate_oneof(self, object: Message, name: str): + if object.WhichOneof(name) is None: + self._validation_response.errors.append( + ValidationError(oneof=f"{object.DESCRIPTOR.name} oneof:{name} must be set") + ) + + def _validate_device_exists(self, name: str): + if name not in self._validation_request.infrastructure.inventory.devices: + self._validation_response.errors.append( + ValidationError(referential_integrity=f"Infrastructure.devices[{name}] does not exist") + ) + + def _validate_device_connection(self, connection: str): + pass + + def _validate_binding_infrastructure_path(self, binding: Binding): + pass + + def _validate_count(self, object: Message): + """Validates that the count of an object is greater than 0""" + if object.count < 1: + self._validation_response.errors.append( + ValidationError( + count=f"{object.DESCRIPTOR.name}.count == {object.count} and must be greater than 0" + ) + ) + + def validate(self, request: ValidationRequest): + """Validate Infrastructure and Bindings. + + Enforces the correctness of messages by validating presence, maps, + oneof and referential integrity that is implied in device/infrastructure + connection paths and binding infrastructure paths. + + Every Device in Infrastructure.inventory.devices has connections which + must have a valid number of pieces separated by a ".". + + Every connection in Infrastructure.connections must be composed of + a valid number of pieces separated by a "." and the pieces must exist + in the Infrastructure.inventory.devices and Infrastructure.inventory.links. + + The format of a Device connection is the following: + "component_name.component_index.link_name.component_name.component_index" + """ + self._validation_request = request + self._validation_response = ValidationResponse() + + self._validate_map(request.infrastructure.inventory.devices) + for device in request.infrastructure.inventory.devices.values(): + self._validate_presence(device, "name") + self._validate_map(device.links) + self._validate_map(device.components) + for link in device.links.values(): + self._validate_presence(link, "name") + for component in device.components.values(): + self._validate_presence(component, "name") + self._validate_presence(component, "count") + self._validate_count(component) + for connection in device.connections: + self._validate_component_connection(device, connection) + for link in device.links.values(): + self._validate_oneof(link.bandwidth, "type") + for link in request.infrastructure.inventory.links: + self._validate_presence(link, "name") + self._validate_map(request.infrastructure.device_instances) + for device_instance in request.infrastructure.device_instances.values(): + self._validate_count(device_instance) + self._validate_device_exists(device_instance.device) + for connection in request.infrastructure.connections: + self._validate_device_connection(connection) + if request.bindings is not None: + for binding in request.bindings.bindings: + self._validate_oneof(binding, "infrastructure_path") + self._validate_binding_infrastructure_path(binding) + return self._validation_response diff --git a/src/zionex.py b/src/zionex.py deleted file mode 100644 index 9cf9b31..0000000 --- a/src/zionex.py +++ /dev/null @@ -1,126 +0,0 @@ -"""ZionEx package - -Uses the infra_pb2 protobuf generated code -to capture components, links, and connections of the -ZionEx block diagram. -""" -from typing import List, Literal, Type, Union - -if __package__ is None or __package__ == "": - import generated.infra_pb2 as infra - import builders as bld -else: - from .generated import infra_pb2 as infra - from . import builders as bld - - -class ZionEx(bld.HostBuilder): - """ZionEx package that contains components""" - - name = "ZionEx host device" - description = "ZionEx host device" - - def __init__(self): - """Create a ZionEx device consisting of components, links and - connections. - """ - super(ZionEx).__init__() - cpu = infra.Component(name="cpu", count=4, cpu=infra.Cpu()) - oam = infra.Component(name="oam", count=8, npu=infra.Npu()) - self._port_component = infra.Component(name="cc-nic", count=8, nic=infra.Nic()) - emerald_pools_pcie_sw = infra.Component( - name="ep-sw", - count=4, - switch=infra.Switch(pcie=infra.Pcie()), - ) - clear_creek_pcie_sw = infra.Component( - name="cc-sw", - count=4, - switch=infra.Switch(pcie=infra.Pcie()), - ) - oam_interconnect = infra.Component( - name="ep-oam-sw", - count=1, - custom=infra.CustomComponent(), - ) - pciev2 = infra.Link(name="pciev2", type=infra.LinkType.LINK_PCIE) - pciev3 = infra.Link(name="pciev3", type=infra.LinkType.LINK_PCIE) - pciev4 = infra.Link(name="pciev4", type=infra.LinkType.LINK_PCIE) - upi_link = infra.Link(name="upi-link", type=infra.LinkType.LINK_UPI) - oam_link = infra.Link(name="oam-link", type=infra.LinkType.LINK_NVLINK) - self._device = infra.Device( - name="zionex", - components={ - cpu.name: cpu, - oam.name: oam, - self._port_component.name: self._port_component, - emerald_pools_pcie_sw.name: emerald_pools_pcie_sw, - clear_creek_pcie_sw.name: clear_creek_pcie_sw, - oam_interconnect.name: oam_interconnect, - }, - links={ - upi_link.name: upi_link, - oam_link.name: oam_link, - pciev2.name: pciev2, - pciev3.name: pciev3, - pciev4.name: pciev4, - }, - ) - for c1_index in range(cpu.count): - for c2_index in range(cpu.count): - if c1_index != c2_index: - self._add_component_link( - cpu.name, - c1_index, - upi_link.name, - cpu.name, - c2_index, - ) - for index in range(cpu.count): - self._add_component_link( - cpu.name, - index, - pciev3.name, - clear_creek_pcie_sw.name, - index, - ) - for index in range(clear_creek_pcie_sw.count): - self._add_component_link( - emerald_pools_pcie_sw.name, - index, - pciev3.name, - clear_creek_pcie_sw.name, - index, - ) - for c1_index in range(self._port_component.count): - self._add_component_link( - self._port_component.name, - c1_index, - pciev3.name, - clear_creek_pcie_sw.name, - int(c1_index / 2), - ) - for c1_index in range(oam.count): - self._add_component_link( - oam.name, - c1_index, - pciev3.name, - emerald_pools_pcie_sw.name, - int(c1_index / 2), - ) - for i in range(oam.count): - self._add_component_link( - oam.name, - i, - oam_link.name, - oam_interconnect.name, - 0, - ) - - @property - def port_up_component(self) -> infra.Component: - return self._port_component - - @property - def port_down_component(self) -> infra.Component: - return None \ No newline at end of file