123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561openSyntaxopenOptimizeopenCoremoduleTbl=Hashtbl.PolymoduletypeFABRIC_GEN=sigtypefabric=policylist*policylistvalgenerate_fabric:?log:bool->?record_paths:string->vrel:pred->vtopo:policy->ving:pred->veg:pred->ptopo:policy->ping:pred->peg:pred->unit->fabricendmoduleFabricGen=structtypefabric=policylist*policylist(* auxilliary list functions *)letintersxsys=List.filterxs~f:(List.mem~equal:Poly.(=)ys)letminimizexsobj=letfbestx=letv=objxinmatchbest,vwith|None,None->None|None,Somev->Some(x,v)|Some(y,v'),None->best|Some(y,v'),Somev->ifPoly.(v<v')thenSome(x,v)elsebestinList.foldxs~init:None~f(* physical location *)typeploc=switchId*portId[@@derivingcompare,eq](* virtual location *)typevloc=vswitchId*vportId[@@derivingcompare,eq](* topology node *)type('a,'b)node=|InPortof'a*'b|OutPortof'a*'b[@@derivingsexp,compare,eq](* virtual vertex *)moduleVV=structtypet=(vswitchId,vportId)node[@@derivingsexp,compare,eq]lethash=Hashtbl.hashend(* physical vertex *)modulePV=structtypet=(switchId,portId)node[@@derivingsexp,compare,eq]lethash=Hashtbl.hashend(* product vertex *)typeprod_vertex=|ConsistentInofVV.t*PV.t|InconsistentOutofVV.t*PV.t|ConsistentOutofVV.t*PV.t|InconsistentInofVV.t*PV.t[@@derivingsexp,compare,eq]moduleV=structtypet=prod_vertex[@@derivingsexp,compare,eq]lethash=Hashtbl.hashend(* Module to build graphs from topologies (physical or virtual) *)moduleGraphBuilder(Params:sigtypeswitch[@@derivingsexp]typeport[@@derivingsexp]vallocs_from_pred:pred->(switch*port)listvallinks_from_topo:policy->(switch*port*switch*port)listend)(Vlabel:Graph.Sig.COMPARABLEwithtypet=(Params.switch,Params.port)node)=structmoduleG=structincludeParamsincludeGraph.Persistent.Digraph.Concrete(Vlabel)letsexp_of_vertexv=sexp_of_nodesexp_of_switchsexp_of_port(V.labelv)letadd_vertex'vg=add_vertexgvletadd_edge'v1v2g=add_edgegv1v2letadd_loc(sw,pt)g=letin_pt=V.create(InPort(sw,pt))inletout_pt=V.create(OutPort(sw,pt))ing|>add_vertex'in_pt|>add_vertex'out_ptletadd_link(sw1,pt1,sw2,pt2)g=g|>add_loc(sw1,pt1)|>add_loc(sw2,pt2)|>add_edge'(V.create(OutPort(sw1,pt1)))(V.create(InPort(sw2,pt2)))letconnect_switch_ports'v1v2g=matchV.labelv1,V.labelv2with|InPort(sw,_),OutPort(sw',_)whenPoly.(sw=sw')->add_edgegv1v2|_->gletconnect_switch_portsg=fold_vertex(funv1g'->fold_vertex(funv2g'->connect_switch_ports'v1v2g')gg')ggletmake(ingress:pred)(egress:pred)(topo:policy)=empty|>Caml.List.fold_rightadd_link(links_from_topotopo)|>Caml.List.fold_rightadd_loc(locs_from_predingress)|>Caml.List.fold_rightadd_loc(locs_from_predegress)|>connect_switch_ports(* dot file encoding *)letgraph_attributesg=[]letdefault_vertex_attributesv=[]letvertex_namev="\""^(Sexp.to_string(sexp_of_vertexv))^"\""letvertex_attributesv=[]letget_subgraphv=letopenGraph.Graphviz.DotAttributesinmatchV.labelvwith|InPort(sw,_)|OutPort(sw,_)->Some{sg_name=Sexp.to_string(sexp_of_switchsw);sg_attributes=[];sg_parent=None}letdefault_edge_attributese=[]letedge_attributese=[]endmoduleDot=Graph.Graphviz.Dot(G)includeGend(* Module holding the three types of graphs we need: virtual, phyiscal, and product graphs *)moduleG=structmoduleVirt=GraphBuilder(structtypeswitch=vswitchId[@@derivingsexp]typeport=vportId[@@derivingsexp]letreclocs_from_predpred=matchpredwith|And(Test(VSwitchvsw),Test(VPortvpt))->[(vsw,vpt)]|Or(p1,p2)->locs_from_predp1@locs_from_predp2|_->failwith"Virtual Compiler: not a valid virtual ingress/egress predicate"letreclinks_from_topovtopo=matchvtopowith|VLink(vsw1,vpt1,vsw2,vpt2)->[(vsw1,vpt1,vsw2,vpt2)]|Union(t1,t2)->links_from_topot1@links_from_topot2|_->ifPoly.(vtopo=drop)then[]elsefailwith("Virtual Compiler: not a valid virtual topology")end)(VV)modulePhys=GraphBuilder(structtypeswitch=switchId[@@derivingsexp]typeport=portId[@@derivingsexp]letreclocs_from_predpred=matchpredwith|And(Test(Switchsw),Test(Location(Physicalpt)))->[(sw,pt)]|Or(p1,p2)->locs_from_predp1@locs_from_predp2|_->failwith"Virtual Compiler: not a valid physical ingress/egress predicate"letreclinks_from_topotopo=matchtopowith|Link(sw1,pt1,sw2,pt2)->[(sw1,pt1,sw2,pt2)]|Union(t1,t2)->links_from_topot1@links_from_topot2|_->ifPoly.(topo=drop)then[]elsefailwith"Virtual Compiler: not a valid physical topology"end)(PV)moduleProd=structmoduleG=structincludeGraph.Persistent.Digraph.Concrete(V)letgraph_attributesg=[]letdefault_vertex_attributesv=[]letvertex_namev="\""^(Sexp.to_string(sexp_of_prod_vertex(V.labelv)))^"\""letvertex_attributesv=[]letget_subgraphv=letopenGraph.Graphviz.DotAttributesinmatchV.labelvwith|ConsistentIn(_,pv)|ConsistentOut(_,pv)|InconsistentIn(_,pv)|InconsistentOut(_,pv)->beginmatchpvwith|InPort(sw,_)|OutPort(sw,_)->Some{sg_name=Sexp.to_string(sexp_of_switchIdsw);sg_attributes=[];sg_parent=None}endletdefault_edge_attributese=[]letedge_attributese=[]endmoduleDot=Graph.Graphviz.Dot(G)includeGendendletparse_vrel(vrel:pred):(vloc,ploclist)Hashtbl.t=letrecparse_physicalpredalist=matchpredwith|Or(p1,p2)->parse_physicalp1alist|>parse_physicalp2|And(Test(Switchsw),Test(Location(Physicalpt)))->(sw,pt)::alist|_->failwith"Virtual Compiler: not a valid virtual relation"inletrecparsepredalist=matchpredwith|Or(p1,p2)->parsep1alist|>parsep2|And(And(Test(VSwitchvsw),Test(VPortvpt)),physical)->((vsw,vpt),parse_physicalphysical[])::alist|_->failwith"Virtual Compiler: not a valid virtual relation"inmatchTbl.of_alist(parsevrel[])with|`Okmap->map|`Duplicate_key(_,_)->failwith"Virtual Compiler: virtual relation contains duplicate key"letget_vrelvrel=letvrel_tbl=parse_vrelvrelinletvrel(vsw,vpt)=Tbl.findvrel_tbl(vsw,vpt)|>Core.Option.value~default:[]in(funvv->matchG.Virt.V.labelvvwith|InPort(vsw,vpt)->vrel(vsw,vpt)|>List.map~f:(fun(sw,pt)->G.Phys.V.create(InPort(sw,pt)))|OutPort(vsw,vpt)->vrel(vsw,vpt)|>List.map~f:(fun(sw,pt)->G.Phys.V.create(OutPort(sw,pt))))letmake_product_graph(vgraph:G.Virt.t)(pgraph:G.Phys.t)(ving:pred)(vrel:pred)=beginletvrel'=get_vrelvrelinletadd_loopvg=matchG.Phys.V.labelvwith|OutPort(sw,pt)->G.Phys.add_edgegv(G.Phys.V.create(InPort(sw,pt)))|_->ginletpgraph_closure=letmoduleOp=Graph.Oper.P(G.Phys)inletclosure=Op.transitive_closure~reflexive:falsepgraphinG.Phys.fold_vertexadd_loopclosureclosureinletvirt_ing=List.map(G.Virt.locs_from_predving)~f:(fun(vsw,vpt)->InPort(vsw,vpt)|>G.Virt.V.create)inletprod_ing=List.mapvirt_ing~f:(funvv->List.cartesian_product[vv](vrel'vv))|>List.concat|>List.map~f:(fun(vv,pv)->G.Prod.V.create(ConsistentIn(vv,pv)))inletstepv=beginmatchG.Prod.V.labelvwith|ConsistentIn(vv,pv)->letvirtual_sucs=G.Virt.succvgraphvvinList.mapvirtual_sucs~f:(funvv->InconsistentOut(vv,pv)|>G.Prod.V.create)|InconsistentOut(vv,pv)->letphysical_sucs=matchvrel'vvwith(* SJS: This is a hack. We interpret [] as true, although to be consistent we would have
to interpret it as false *)|[]->G.Phys.succpgraph_closurepv|logical_sucs->interslogical_sucs(G.Phys.succpgraph_closurepv)inList.mapphysical_sucs~f:(funpsuc->ConsistentOut(vv,psuc)|>G.Prod.V.create)|ConsistentOut(vv,pv)->(* SJS: check that if there are no successors, we have reached the egress *)letvirtual_sucs=G.Virt.succvgraphvvinList.mapvirtual_sucs~f:(funvsuc->InconsistentIn(vsuc,pv)|>G.Prod.V.create)|InconsistentIn(vv,pv)->letphysical_sucs=matchvrel'vvwith(* SJS: This is a hack. We interpret [] as true, although to be consistent we would have
to interpret it as false *)|[]->G.Phys.succpgraph_closurepv|logical_sucs->interslogical_sucs(G.Phys.succpgraph_closurepv)inList.mapphysical_sucs~f:(funpv->ConsistentIn(vv,pv)|>G.Prod.V.create)endinletrecmakework_listedgesg=beginmatchwork_listwith|[]->(* add edges after all vertices are inserted *)List.foldedges~init:g~f:(fung(v1,v2)->G.Prod.add_edgegv1v2)|v::vs->ifG.Prod.mem_vertexgvthenmakevsedgesgelseletg'=G.Prod.add_vertexgvinletsucs=stepvinletedges'=List.foldsucs~init:edges~f:(funedgessuc->(v,suc)::edges)inmake(sucs@work_list)edges'g'endin(prod_ing,makeprod_ing[](G.Prod.empty))end(* The fabric has to ensure that no matter what the programmer does, it can always restore
consistency. This function eliminates all paths that allow the programmer to step to a
unrepairable state. *)letprune_product_graphg=(* an inconsistent location in the product graph is "fatal" if restoring consistency is
impossible; here we exlude nodes that are fatal "by transitivity" *)letis_fatalvg=matchG.Prod.V.labelvwith|InconsistentIn_|InconsistentOut_->G.Prod.out_degreegv=0|_->falsein(* erases fatal node and all its predecessors that are fatal by transitivity *)letrecerase_fatalvg=matchG.Prod.V.labelvwith|InconsistentIn_|InconsistentOut_->letg'=G.Prod.remove_vertexgvinG.Prod.fold_prederase_fatalgvg'|ConsistentOut_|ConsistentIn_->letg'=G.Prod.remove_vertexgvinG.Prod.fold_pred(funvg->ifis_fatalvgthenerase_fatalvgelseg)gvg'inG.Prod.fold_vertex(funvg->ifis_fatalvgthenerase_fatalvgelseg)gg(* The pruned graph may leave the fabric with several options to restore consistency; to arrive at
a fabric graph, we must decide on a single option wherever we have a choice, thus determining a
fabric uniquely.
This function implements a greedy algorithm that makes this choice by minimizing the cost of the
selection at each step, yielding a fabric valid for ingress ing. *)letfabric_graph_of_prunedgingcost=letrecselectvg'=ifG.Prod.mem_vertexg'vtheng'elseletg'=G.Prod.add_vertexg'vinmatchG.Prod.V.labelvwith|ConsistentIn_|ConsistentOut_->G.Prod.fold_succ(select'v)gvg'|InconsistentIn_|InconsistentOut_->letsucs=G.Prod.succgvinbeginmatchminimizesucs(funv'->costvv')with|None->assertfalse(*no alernate path*)|Some(selection,_)->select'vselectiong'endandselect'vv'g'=G.Prod.add_edge(selectv'g')vv'inCaml.List.fold_rightselectingG.Prod.empty(* functions for fabric generation *)letmatch_ploc(sw,pt)=Filter(And(Test(Switchsw),Test(Location(Physical(pt)))))letmatch_vloc(vsw,vpt)=Filter(And(Test(VSwitchvsw),Test(VPortvpt)))letset_vloc(vsw,vpt)=mk_seq(Mod(VSwitchvsw))(Mod(VPortvpt))letmatch_vloc'vv=matchG.Virt.V.labelvvwith|InPort(vsw,vpt)|OutPort(vsw,vpt)->match_vloc(vsw,vpt)letmatch_ploc'pv=matchG.Phys.V.labelpvwith|InPort(sw,pt)|OutPort(sw,pt)->match_ploc(sw,pt)letset_vloc'vv=matchG.Virt.V.labelvvwith|InPort(vsw,vpt)|OutPort(vsw,vpt)->set_vloc(vsw,vpt)letrecpol_of_pathpath=matchpathwith|(OutPort(sw1,pt1),(InPort(sw2,pt2)))::path'->ifPoly.(sw1=sw2)thenbeginassertPoly.(pt1=pt2);pol_of_pathpath'endelsemk_seq(Link(sw1,pt1,sw2,pt2))(pol_of_pathpath')|(InPort(sw,pt),(OutPort(sw',pt')))::path'->assertPoly.(sw=sw');ifPoly.(pt=pt')thenpol_of_pathpath'elsemk_seq(Mod(Location(Physical(pt'))))(pol_of_pathpath')|[]->id|_->assertfalseletrecprint_pathpathout_channel=matchpathwith|(OutPort(sw1,_),(InPort(sw2,_)))::path'->ifPoly.(sw1=sw2)thenprint_pathpath'out_channelelse(Printf.fprintfout_channel"%Lu-%Lu"sw1sw2;print_pathpath'out_channel)|(InPort(sw,_),(OutPort(sw',_)))::path'->Printf.fprintfout_channel" ";print_pathpath'out_channel|[]->Printf.fprintfout_channel"\n%!";|_->assertfalseletfabric_atom_of_prod_edge?record_pathspath_oraclev1v2=matchG.Prod.V.labelv1,G.Prod.V.labelv2with|ConsistentOut_,InconsistentIn_|ConsistentIn_,InconsistentOut_->`None|(InconsistentOut(vv,pv1)asl),ConsistentOut(vv',pv2)|(InconsistentIn(vv,pv1)asl),ConsistentIn(vv',pv2)->assertPoly.(vv=vv');letpath=path_oraclepv1pv2inlet_=Core.Option.(record_paths>>|print_pathpath)inletfabric=[match_vloc'vv;match_ploc'pv1;pol_of_pathpath;set_vloc'vv]|>mk_big_seqinbeginmatchlwith|InconsistentOut_->`Outfabric|InconsistentIn_->`Infabric|_->assertfalseend|_->assertfalseletfabric_of_fabric_graph?record_pathsgingpath_oracle=ifnot(List.for_alling~f:(G.Prod.mem_vertexg))thenfailwith"virtual compiler: specification allows for no valid fabric"elseletrecord_paths=Core.Option.(record_paths>>|Out_channel.create)inletfv1v2((fout,fin)asfs)=matchfabric_atom_of_prod_edge?record_pathspath_oraclev1v2with|`None->fs|`Outf->(f::fout,fin)|`Inf->(fout,f::fin)inletfabric=G.Prod.fold_edgesfg([],[])inlet_=Core.Option.(record_paths>>|Out_channel.close)infabricletdefault_ving_pol~vrel~ping:policyoption=letvrel':(vloc,ploclist)Hashtbl.t=parse_vrelvrelinletvrel:(ploc,vloclist)Hashtbl.t=Tbl.create()inTbl.iterivrel'~f:(fun~key:v~data:ps->List.iterps~f:(funp->Tbl.add_multivrel~key:p~data:v));letopenOptimizeintryG.Phys.locs_from_predping|>List.map~f:(funploc->matchTbl.findvrelplocwith|Some[vloc]->mk_seq(match_plocploc)(set_vlocvloc)|_->failwith"vrel must map physical ingress uniquely to virtual ingress")|>mk_big_union|>Option.somewith|Failure_->Noneletgenerate_fabric?(log=true)?record_paths~vrel~vtopo~ving~veg~ptopo~ping~peg()=letvgraph=G.Virt.makevingvegvtopoinletpgraph=G.Phys.makepingpegptopoinletprod_ing,prod_graph=make_product_graphvgraphpgraphvingvrelinletunwrap_ee=(G.Phys.V.label(G.Phys.E.srce),G.Phys.V.label(G.Phys.E.dste))inletunwrap_pathpath=List.mappath~f:unwrap_einletmoduleWEIGHT=structtypeedge=G.Phys.E.ttypet=intletweighte=matchunwrap_eewith|InPort_,OutPort_->0|OutPort_,InPort_->1|_,_->assertfalseletcompare=compareletaddxy=x+yletzero=0endinletmoduleDijkstra=Graph.Path.Dijkstra(G.Phys)(WEIGHT)inletdist_tbl=Tbl.create()inletis_looppv1pv2=matchpv1,pv2with|OutPort(sw,_),InPort(sw',_)->Poly.(sw=sw')|_->falseinletget_path_and_distancepv1pv2=ifis_looppv1pv2thenSome([],0)elsematchTbl.finddist_tbl(pv1,pv2)with|None->(* FIXME: temporary hack to avoid Jane Street's annoying warnings. *)begin[@warning"-3"]tryletpath',dist=Dijkstra.shortest_pathpgraphpv1pv2inletpath=unwrap_pathpath'inTbl.setdist_tbl~key:(pv1,pv2)~data:(path,dist);Some(path,dist)withNot_found|Not_found_s_->Noneend|pd->pdinletpath_oraclepv1pv2=matchget_path_and_distancepv1pv2with|Some(p,d)->p|None->assertfalseinletpv_of_vv=matchG.Prod.V.labelvwith|InconsistentIn(_,pv)|InconsistentOut(_,pv)|ConsistentIn(_,pv)|ConsistentOut(_,pv)->pvinletcostv1v2=matchget_path_and_distance(pv_of_vv1)(pv_of_vv2)with|Some(p,d)->Somep|None->Noneinletpruned_graph=lazy(prune_product_graphprod_graph)inletfabric_graph=lazy(fabric_graph_of_pruned(Lazy.forcepruned_graph)prod_ingcost)inletfabric=lazy(fabric_of_fabric_graph?record_paths(Lazy.forcefabric_graph)prod_ingpath_oracle)inletvg_file="vg.dot"inletpg_file="pg.dot"inletg_raw_file="g_raw.dot"inletg_pruned_file="g_pruned.dot"inletg_fabric_file="g_fabric.dot"inletvg_ch=Out_channel.createvg_fileinletpg_ch=Out_channel.createpg_fileinletg_raw_ch=Out_channel.createg_raw_fileinletg_pruned_ch=Out_channel.createg_pruned_fileinletg_fabric_ch=Out_channel.createg_fabric_fileiniflogthenbeginPrintf.printf"[virtual] Statistics:\n";Printf.printf" |V(vgraph)|: %i\n"(G.Virt.nb_vertexvgraph);Printf.printf" |E(vgraph)|: %i\n"(G.Virt.nb_edgesvgraph);G.Virt.Dot.output_graphvg_chvgraph;Out_channel.closevg_ch;Printf.printf" |V(pgraph)|: %i\n"(G.Phys.nb_vertexpgraph);Printf.printf" |E(pgraph)|: %i\n"(G.Phys.nb_edgespgraph);G.Phys.Dot.output_graphpg_chpgraph;Out_channel.closepg_ch;Printf.printf" |V(prod_graph)|: %i\n"(G.Prod.nb_vertexprod_graph);Printf.printf" |E(prod_graph)|: %i\n"(G.Prod.nb_edgesprod_graph);G.Prod.Dot.output_graphg_raw_chprod_graph;Out_channel.closeg_raw_ch;Printf.printf" |V(pruned_graph)|: %i\n"(G.Prod.nb_vertex(Lazy.forcepruned_graph));Printf.printf" |E(pruned_graph)|: %i\n"(G.Prod.nb_edges(Lazy.forcepruned_graph));G.Prod.Dot.output_graphg_pruned_ch(Lazy.forcepruned_graph);Out_channel.closeg_pruned_ch;Printf.printf" |V(fabric_graph)|: %i\n"(G.Prod.nb_vertex(Lazy.forcefabric_graph));Printf.printf" |E(fabric_graph)|: %i\n"(G.Prod.nb_edges(Lazy.forcefabric_graph));G.Prod.Dot.output_graphg_fabric_ch(Lazy.forcefabric_graph);Out_channel.closeg_fabric_ch;Printf.printf"\n";end;Lazy.forcefabricend