package org.batfish.z3;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.batfish.job.BatfishJob;
import org.batfish.main.Settings;
import org.batfish.common.BatfishException;
import org.batfish.common.Pair;
import org.batfish.datamodel.Flow;
import org.batfish.datamodel.collections.NodeVrfSet;
import com.microsoft.z3.BitVecExpr;
import com.microsoft.z3.BitVecNum;
import com.microsoft.z3.BoolExpr;
import com.microsoft.z3.Context;
import com.microsoft.z3.Expr;
import com.microsoft.z3.Fixedpoint;
import com.microsoft.z3.FuncDecl;
import com.microsoft.z3.Model;
import com.microsoft.z3.Params;
import com.microsoft.z3.Solver;
import com.microsoft.z3.Status;
import com.microsoft.z3.Z3Exception;
public class CompositeNodJob extends BatfishJob<NodJobResult> {
private List<Synthesizer> _dataPlaneSynthesizers;
private final NodeVrfSet _nodeVrfSet;
private int _numPrograms;
private List<QuerySynthesizer> _querySynthesizers;
private String _tag;
public CompositeNodJob(Settings settings,
List<Synthesizer> dataPlaneSynthesizer,
List<QuerySynthesizer> querySynthesizer, NodeVrfSet nodeVrfSet,
String tag) {
super(settings);
_numPrograms = dataPlaneSynthesizer.size();
if (_numPrograms != querySynthesizer.size()) {
throw new BatfishException(
"mismatch between number of programs and number of queries");
}
_dataPlaneSynthesizers = dataPlaneSynthesizer;
_querySynthesizers = querySynthesizer;
_nodeVrfSet = new NodeVrfSet();
_nodeVrfSet.addAll(nodeVrfSet);
_tag = tag;
}
@Override
public NodJobResult call() throws Exception {
long startTime = System.currentTimeMillis();
long elapsedTime;
NodProgram latestProgram = null;
try (Context ctx = new Context()) {
BoolExpr[] answers = new BoolExpr[_numPrograms];
Params p = ctx.mkParams();
p.add("fixedpoint.engine", "datalog");
p.add("fixedpoint.datalog.default_relation", "doc");
p.add("fixedpoint.print_answer", true);
for (int i = 0; i < _numPrograms; i++) {
Synthesizer dataPlaneSynthesizer = _dataPlaneSynthesizers.get(i);
QuerySynthesizer querySynthesizer = _querySynthesizers.get(i);
NodProgram baseProgram = dataPlaneSynthesizer
.synthesizeNodDataPlaneProgram(ctx);
NodProgram queryProgram = querySynthesizer
.getNodProgram(baseProgram);
NodProgram program = baseProgram.append(queryProgram);
latestProgram = program;
Fixedpoint fix = ctx.mkFixedpoint();
fix.setParameters(p);
for (FuncDecl relationDeclaration : program
.getRelationDeclarations().values()) {
fix.registerRelation(relationDeclaration);
}
for (BoolExpr rule : program.getRules()) {
fix.addRule(rule, null);
}
for (BoolExpr query : program.getQueries()) {
Status status = fix.query(query);
switch (status) {
case SATISFIABLE:
break;
case UNKNOWN:
throw new BatfishException("Query satisfiability unknown");
case UNSATISFIABLE:
break;
default:
throw new BatfishException("invalid status");
}
}
Expr answer = fix.getAnswer();
BoolExpr solverInput;
if (answer.getArgs().length > 0) {
List<Expr> reversedVarList = new ArrayList<>();
reversedVarList.addAll(program.getVariablesAsConsts().values());
Collections.reverse(reversedVarList);
Expr[] reversedVars = reversedVarList.toArray(new Expr[] {});
Expr substitutedAnswer = answer.substituteVars(reversedVars);
solverInput = (BoolExpr) substitutedAnswer;
}
else {
solverInput = (BoolExpr) answer;
}
if (_querySynthesizers.get(i).getNegate()) {
answers[i] = ctx.mkNot(solverInput);
}
else {
answers[i] = solverInput;
}
}
BoolExpr compositeQuery = ctx.mkAnd(answers);
Solver solver = ctx.mkSolver();
solver.add(compositeQuery);
Status solverStatus = solver.check();
switch (solverStatus) {
case SATISFIABLE:
break;
case UNKNOWN:
throw new BatfishException("Stage 2 query satisfiability unknown");
case UNSATISFIABLE:
elapsedTime = System.currentTimeMillis() - startTime;
return new NodJobResult(elapsedTime, _logger.getHistory());
default:
throw new BatfishException("invalid status");
}
Model model = solver.getModel();
Map<String, Long> constraints = new LinkedHashMap<>();
for (FuncDecl constDecl : model.getConstDecls()) {
String name = constDecl.getName().toString();
BitVecExpr varConstExpr = latestProgram.getVariablesAsConsts()
.get(name);
long val = ((BitVecNum) model.getConstInterp(varConstExpr))
.getLong();
constraints.put(name, val);
}
Set<Flow> flows = new HashSet<>();
for (Pair<String, String> nodeVrf : _nodeVrfSet) {
String node = nodeVrf.getFirst();
String vrf = nodeVrf.getSecond();
Flow flow = createFlow(node, vrf, constraints);
flows.add(flow);
}
elapsedTime = System.currentTimeMillis() - startTime;
return new NodJobResult(elapsedTime, _logger.getHistory(), flows);
}
catch (Z3Exception e) {
elapsedTime = System.currentTimeMillis() - startTime;
return new NodJobResult(elapsedTime, _logger.getHistory(),
new BatfishException(
"Error running NoD on concatenated data plane", e));
}
}
private Flow createFlow(String node, String vrf,
Map<String, Long> constraints) {
return NodJob.createFlow(node, vrf, constraints, _tag);
}
}