376126b349465eb4d297e4f050eb7cd6f34b5a39
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / DestinationMapper.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
10
11 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ARP;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.IPv4;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.IPv6;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch;
15 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
16 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.createNodePath;
17 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.decNwTtlAction;
18 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ethernetMatch;
19 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.getOfPortNum;
20 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
21 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.groupAction;
22 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
23 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpOpAction;
24 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpShaAction;
25 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpSpaAction;
26 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadRegAction;
27 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIPv4Action;
28 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction;
29 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpShaToArpThaAction;
30 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpSpaToArpTpaAction;
31 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveEthSrcToEthDstAction;
32 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction;
33 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlDstAction;
34 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlSrcAction;
35
36 import java.math.BigInteger;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Map.Entry;
45 import java.util.Objects;
46 import java.util.Set;
47
48 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
49 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
50 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
51 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
52 import org.opendaylight.groupbasedpolicy.endpoint.EpKey;
53 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
54 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.FlowMap;
55 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
56 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
57 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
58 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
59 import org.opendaylight.groupbasedpolicy.resolver.TenantUtils;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
103 import org.opendaylight.yangtools.yang.binding.DataObject;
104 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107
108 import com.google.common.base.Optional;
109 import com.google.common.collect.HashMultimap;
110 import com.google.common.collect.SetMultimap;
111 import com.google.common.collect.Sets;
112 import com.google.common.util.concurrent.CheckedFuture;
113
114 /**
115  * Manage the table that maps the destination address to the next hop for the
116  * path as well as applies any relevant routing transformations.
117  */
118 public class DestinationMapper extends FlowTable {
119
120     protected static final Logger LOG = LoggerFactory.getLogger(DestinationMapper.class);
121
122     // TODO Li alagalah: Improve UT coverage for this class.
123
124     // TODO Li alagalah: Use EndpointL3 for L3 flows, Endpoint for L2 flows
125     // This ensures we have the appropriate network-containment'
126
127     public static final short TABLE_ID = 2;
128     /**
129      * This is the MAC address of the magical router in the sky
130      */
131     public static final MacAddress ROUTER_MAC = new MacAddress("88:f0:31:b5:12:b5");
132     public static final MacAddress MULTICAST_MAC = new MacAddress("01:00:00:00:00:00");
133
134     public DestinationMapper(OfContext ctx) {
135         super(ctx);
136     }
137
138     Map<TenantId, HashSet<Subnet>> subnetsByTenant = new HashMap<TenantId, HashSet<Subnet>>();
139
140     @Override
141     public short getTableId() {
142         return TABLE_ID;
143     }
144
145     @Override
146     public void sync(NodeId nodeId, PolicyInfo policyInfo, FlowMap flowMap) throws Exception {
147
148         TenantId currentTenant;
149
150         flowMap.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(1), null));
151
152         SetMultimap<EpKey, EpKey> visitedEps = HashMultimap.create();
153         Set<EndpointFwdCtxOrdinals> epOrdSet = new HashSet<>();
154
155         for (Endpoint srcEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
156             Set<EndpointGroupId> srcEpgIds = new HashSet<>();
157             if (srcEp.getEndpointGroup() != null)
158                 srcEpgIds.add(srcEp.getEndpointGroup());
159             if (srcEp.getEndpointGroups() != null)
160                 srcEpgIds.addAll(srcEp.getEndpointGroups());
161
162             for (EndpointGroupId epgId : srcEpgIds) {
163                 EgKey epg = new EgKey(srcEp.getTenant(), epgId);
164                 Set<EgKey> peers = Sets.union(Collections.singleton(epg), policyInfo.getPeers(epg));
165                 for (EgKey peer : peers) {
166                     for (Endpoint peerEp : ctx.getEndpointManager().getEndpointsForGroup(peer)) {
167                         currentTenant = peerEp.getTenant();
168                         subnetsByTenant.put(currentTenant, getSubnets(currentTenant));
169                         EpKey srcEpKey = new EpKey(srcEp.getL2Context(), srcEp.getMacAddress());
170                         EpKey peerEpKey = new EpKey(peerEp.getL2Context(), peerEp.getMacAddress());
171
172                         if (visitedEps.get(srcEpKey) != null && visitedEps.get(srcEpKey).contains(peerEpKey)) {
173                             continue;
174                         }
175                         syncEP(flowMap, nodeId, policyInfo, srcEp, peerEp);
176                         visitedEps.put(srcEpKey, peerEpKey);
177
178                         // Process subnets and flood-domains for epPeer
179                         EndpointFwdCtxOrdinals epOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo,
180                                 peerEp);
181                         epOrdSet.add(epOrds);
182                     }
183                 }
184             }
185         }
186
187         for (Entry<TenantId, HashSet<Subnet>> subnetEntry : subnetsByTenant.entrySet()) {
188             if (subnetEntry.getValue() == null) {
189                 LOG.trace("Tenant: {} has empty subnet entry.", subnetEntry.getKey());
190                 continue;
191             }
192             currentTenant = subnetEntry.getKey();
193             for (Subnet sn : subnetEntry.getValue()) {
194                 L3Context l3c = getL3ContextForSubnet(currentTenant, sn);
195                 Flow arpFlow = createRouterArpFlow(currentTenant, nodeId, sn,
196                         OrdinalFactory.getContextOrdinal(currentTenant, l3c.getId()));
197                 if (arpFlow != null) {
198                     flowMap.writeFlow(nodeId, TABLE_ID, arpFlow);
199                 } else {
200                     LOG.debug(
201                             "Gateway ARP flow is not created, because virtual router IP has not been set for subnet {} .",
202                             sn.getIpPrefix().getValue());
203                 }
204             }
205         }
206
207         // Write broadcast flows per flood domain.
208         for (EndpointFwdCtxOrdinals epOrd : epOrdSet) {
209             if (groupExists(nodeId, epOrd.getFdId())) {
210                 flowMap.writeFlow(nodeId, TABLE_ID, createBroadcastFlow(epOrd));
211             }
212         }
213     }
214
215     // set up next-hop destinations for all the endpoints in the endpoint
216     // group on the node
217
218     private Flow createBroadcastFlow(EndpointFwdCtxOrdinals epOrd) {
219         FlowId flowId = new FlowId(new StringBuilder().
220                                     append("broadcast|").
221                                     append(epOrd.getFdId()).
222                                     toString());
223         MatchBuilder mb = new MatchBuilder()
224                             .setEthernetMatch(new EthernetMatchBuilder()
225                             .setEthernetDestination(new EthernetDestinationBuilder().
226                                                         setAddress(MULTICAST_MAC)
227                                                         .setMask(MULTICAST_MAC).build())
228                             .build());
229         addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class, Long.valueOf(epOrd.getFdId())));
230
231         FlowBuilder flowb = base().setPriority(Integer.valueOf(140))
232             .setId(flowId)
233             .setMatch(mb.build())
234             .setInstructions(
235                     instructions(applyActionIns(nxLoadTunIdAction(BigInteger.valueOf(epOrd.getFdId()), false),
236                             groupAction(Long.valueOf(epOrd.getFdId())))));
237         return flowb.build();
238     }
239
240     private boolean groupExists(NodeId nodeId, Integer fdId) throws Exception {
241         // Fetch existing GroupTables
242         if (ctx.getDataBroker() == null) {
243             return false;
244         }
245
246         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
247         InstanceIdentifier<Node> niid = createNodePath(nodeId);
248         Optional<Node> r = t.read(LogicalDatastoreType.CONFIGURATION, niid).get();
249         if (!r.isPresent())
250             return false;
251         FlowCapableNode fcn = r.get().getAugmentation(FlowCapableNode.class);
252         if (fcn == null)
253             return false;
254
255         if (fcn.getGroup() != null) {
256             for (Group g : fcn.getGroup()) {
257                 if (g.getGroupId().getValue().equals(Long.valueOf(fdId))) { // Group
258                                                                             // Exists.
259                     return true;
260                 }
261             }
262         }
263         return false;
264     }
265
266     private MacAddress routerPortMac(L3Context l3c, IpAddress ipAddress) {
267
268         if (ctx.getDataBroker() == null) {
269             return null;
270         }
271
272         MacAddress defaultMacAddress = ROUTER_MAC;
273
274         EndpointL3Key l3Key = new EndpointL3Key(ipAddress, l3c.getId());
275         InstanceIdentifier<EndpointL3> endpointsIid = InstanceIdentifier.builder(Endpoints.class)
276             .child(EndpointL3.class, l3Key)
277             .build();
278         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
279
280         Optional<EndpointL3> r;
281         try {
282             r = t.read(LogicalDatastoreType.OPERATIONAL, endpointsIid).get();
283             if (!r.isPresent())
284                 return defaultMacAddress;
285             EndpointL3 epL3 = r.get();
286             if (epL3.getMacAddress() == null) {
287                 return defaultMacAddress;
288             } else {
289                 return epL3.getMacAddress();
290             }
291         } catch (Exception e) {
292             LOG.error("Error reading EndpointL3 {}.{}", l3c, ipAddress, e);
293             return null;
294         }
295     }
296
297     private L3Context getL3ContextForSubnet(TenantId tenantId, Subnet sn) {
298         L3Context l3c = ctx.getPolicyResolver().getTenant(tenantId).resolveL3Context(sn.getId());
299         return l3c;
300     }
301
302     private Flow createRouterArpFlow(TenantId tenantId, NodeId nodeId, Subnet sn, int l3Id) {
303         if (sn == null || sn.getVirtualRouterIp() == null) {
304             LOG.trace("Didn't create routerArpFlow since either subnet or subnet virtual router was null");
305             return null;
306         }
307         /*
308          * TODO: Li alagalah: This should be new Yang "gateways" list as well,
309          * that expresses the gateway and prefixes it is interface for. Should
310          * also check for external.
311          */
312         if (sn.getVirtualRouterIp().getIpv4Address() != null) {
313             String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue();
314
315             L3Context l3c = getL3ContextForSubnet(tenantId, sn);
316             if (l3c == null) {
317                 LOG.error("No L3 Context found associated with subnet {}", sn.getId());
318             }
319
320             MacAddress routerMac = routerPortMac(l3c, sn.getVirtualRouterIp());
321             if (routerMac == null) {
322                 return null;
323             }
324
325             BigInteger intRouterMac = new BigInteger(1, bytesFromHexString(routerMac.getValue()));
326
327             FlowId flowId = new FlowId(new StringBuffer().append("routerarp|")
328                 .append(sn.getId().getValue())
329                 .append("|")
330                 .append(ikey)
331                 .append("|")
332                 .append(l3Id)
333                 .toString());
334             MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, ARP)).setLayer3Match(
335                     new ArpMatchBuilder().setArpOp(Integer.valueOf(1))
336                         .setArpTargetTransportAddress(new Ipv4Prefix(ikey + "/32"))
337                         .build());
338             addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(l3Id)));
339
340             FlowBuilder flowb = base().setPriority(150)
341                 .setId(flowId)
342                 .setMatch(mb.build())
343                 .setInstructions(
344                         instructions(applyActionIns(nxMoveEthSrcToEthDstAction(), setDlSrcAction(routerMac),
345                                 nxLoadArpOpAction(BigInteger.valueOf(2L)), nxMoveArpShaToArpThaAction(),
346                                 nxLoadArpShaAction(intRouterMac), nxMoveArpSpaToArpTpaAction(),
347                                 nxLoadArpSpaAction(ikey), outputAction(new NodeConnectorId(nodeId.getValue()
348                                         + ":INPORT")))));
349             return flowb.build();
350         } else {
351             LOG.warn("IPv6 virtual router {} for subnet {} not supported", sn.getVirtualRouterIp(), sn.getId()
352                 .getValue());
353             return null;
354         }
355
356     }
357
358     private Flow createLocalL2Flow(Endpoint ep, EndpointFwdCtxOrdinals epFwdCtxOrds, OfOverlayContext ofc) {
359
360         // TODO Li alagalah - refactor common code but keep simple method
361         ArrayList<Instruction> instructions = new ArrayList<>();
362         List<Action> applyActions = new ArrayList<>();
363
364         int order = 0;
365
366         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
367         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
368         Action setNextHop;
369         String nextHop;
370
371         // BEGIN L2 LOCAL
372         nextHop = ofc.getNodeConnectorId().getValue();
373
374         long portNum;
375         try {
376             portNum = getOfPortNum(ofc.getNodeConnectorId());
377         } catch (NumberFormatException ex) {
378             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
379             return null;
380         }
381
382         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
383
384         // END L2 LOCAL
385
386         order += 1;
387         applyActions.add(setdEPG);
388         applyActions.add(setdCG);
389         applyActions.add(setNextHop);
390         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
391             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
392             .build();
393         instructions.add(applyActionsIns);
394
395         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
396             .setInstruction(gotoTableIns((short) (getTableId() + 1)))
397             .build();
398         instructions.add(gotoTable);
399
400         FlowId flowid = new FlowId(new StringBuilder().append(epFwdCtxOrds.getBdId())
401             .append("|l2|")
402             .append(ep.getMacAddress().getValue())
403             .append("|")
404             .append(nextHop)
405             .toString());
406         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, ep.getMacAddress(), null));
407         addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(epFwdCtxOrds.getBdId())));
408         FlowBuilder flowb = base().setId(flowid)
409             .setPriority(Integer.valueOf(50))
410             .setMatch(mb.build())
411             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
412         return flowb.build();
413     }
414
415     private void syncEP(FlowMap flowMap, NodeId nodeId, PolicyInfo policyInfo, Endpoint srcEp, Endpoint destEp)
416             throws Exception {
417
418         // TODO: Conditions messed up, but for now, send policyInfo until this
419         // is fixed.
420         EndpointFwdCtxOrdinals destEpFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, destEp);
421         EndpointFwdCtxOrdinals srcEpFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, srcEp);
422
423         if (destEp.getTenant() == null || (destEp.getEndpointGroup() == null && destEp.getEndpointGroups() == null)) {
424             LOG.trace("Didn't process endpoint due to either tenant, or EPG(s) being null", destEp.getKey());
425             return;
426         }
427         OfOverlayContext ofc = destEp.getAugmentation(OfOverlayContext.class);
428
429         // ////////////////////////////////////////////////////////////////////////////////////////
430         /*
431          * NOT HANDLING EXTERNALS TODO: alagalah Li: External Gateway
432          * functionality needed here.
433          */
434         if (LocationType.External.equals(ofc.getLocationType())) {
435             // XXX - TODO - perform NAT and send to the external network
436             // TODO: Use case neutron gateway interface
437             LOG.warn("External endpoints not yet supported");
438             return;
439         }
440
441         /*
442          * Only care about subnets for L3, but fetch them before loop. We need
443          * the local subnets for setting SRC MAC for routing. All Routing is now
444          * done locally! YAY! Instead of being shovelled L2 style across network
445          * ala Helium.
446          */
447         List<Subnet> localSubnets = getLocalSubnets(nodeId);
448         if (localSubnets == null) {
449             LOG.error("No subnets could be found locally for node: {}", nodeId);
450             return;
451         }
452
453         if (Objects.equals(ofc.getNodeId(), nodeId)) {
454             // this is a local endpoint; send to the approppriate local
455             // port
456
457             if (srcEpFwdCtxOrds.getBdId() == destEpFwdCtxOrds.getBdId()) {
458                 flowMap.writeFlow(nodeId, TABLE_ID, createLocalL2Flow(destEp, destEpFwdCtxOrds, ofc));
459             }
460             // TODO Li alagalah: Need to move to EndpointL3 for L3 processing.
461             // The Endpoint conflation must end!
462             if (destEp.getL3Address() == null) {
463                 LOG.trace("Endpoint {} didn't have L3 Address so was not processed for L3 flows.", destEp.getKey());
464                 return;
465             }
466
467             for (L3Address l3a : destEp.getL3Address()) {
468                 if (l3a.getIpAddress() == null || l3a.getL3Context() == null) {
469                     LOG.error("Endpoint with L3Address but either IPAddress or L3Context is null. {}",
470                             destEp.getL3Address());
471                     continue;
472                 } else {
473                     for (Subnet localSubnet : localSubnets) {
474                         Flow flow = createLocalL3RoutedFlow(destEp, l3a, destEpFwdCtxOrds, ofc, localSubnet);
475                         if (flow != null) {
476                             flowMap.writeFlow(nodeId, TABLE_ID, flow);
477                         } else {
478                             LOG.trace("Did not write remote L3 flow for endpoint {} and subnet {}", l3a.getIpAddress(),
479                                     localSubnet.getIpPrefix().getValue());
480                         }
481                     }
482                 }
483             }
484         } else {
485             // this endpoint is on a different switch; send to the
486             // appropriate tunnel
487             if (srcEpFwdCtxOrds.getBdId() == destEpFwdCtxOrds.getBdId()) {
488                 Flow remoteL2Flow = createRemoteL2Flow(destEp, nodeId, srcEpFwdCtxOrds, destEpFwdCtxOrds, ofc);
489                 if (remoteL2Flow != null) {
490                     flowMap.writeFlow(nodeId, TABLE_ID, remoteL2Flow);
491                 }
492             } else {
493                 LOG.trace("DestinationMapper: RemoteL2Flow: not created, in different BDs src: {} dst: {}",
494                         srcEpFwdCtxOrds.getBdId(), destEpFwdCtxOrds.getBdId());
495             }
496
497             // TODO Li alagalah: Need to move to EndpointL3 for L3 processing.
498             // The Endpoint conflation must end!
499             if (destEp.getL3Address() == null) {
500                 LOG.trace("Endpoint {} didn't have L3 Address so was not processed for L3 flows.", destEp.getKey());
501                 return;
502             }
503             for (L3Address l3a : destEp.getL3Address()) {
504                 if (l3a.getIpAddress() == null || l3a.getL3Context() == null) {
505                     LOG.error("Endpoint with L3Address but either IPAddress or L3Context is null. {}",
506                             destEp.getL3Address());
507                     continue;
508                 } else {
509                     for (Subnet localSubnet : localSubnets) {
510                         Flow remoteL3Flow = createRemoteL3RoutedFlow(destEp, l3a, nodeId, srcEpFwdCtxOrds,
511                                 destEpFwdCtxOrds, ofc, localSubnet);
512                         if (remoteL3Flow != null) {
513                             flowMap.writeFlow(nodeId, TABLE_ID, remoteL3Flow);
514                         } else {
515                             LOG.trace("Did not write remote L3 flow for endpoint {} and subnet {}", l3a.getIpAddress(),
516                                     localSubnet.getIpPrefix().getValue());
517                         }
518                     }
519                 }
520             }
521         } // remote (tunnel)
522
523         // }
524
525     }
526
527     /*
528      * ################################## DestMapper Flow methods
529      * ##################################
530      */
531     private Flow createLocalL3RoutedFlow(Endpoint destEp, L3Address destL3Address, EndpointFwdCtxOrdinals epFwdCtxOrds,
532             OfOverlayContext ofc, Subnet srcSubnet) {
533
534         // TODO Li alagalah - refactor common code but keep simple method
535
536         Subnet destSubnet = null;
537         HashSet<Subnet> subnets = getSubnets(destEp.getTenant());
538         if (subnets == null) {
539             LOG.trace("No subnets in tenant {}", destL3Address.getIpAddress());
540             return null;
541         }
542         NetworkDomainId epNetworkContainment = getEPNetworkContainment(destEp);
543         for (Subnet subnet : subnets) {
544             // TODO Li alagalah add IPv6 support
545             if (subnet.getId().getValue().equals(epNetworkContainment.getValue())) {
546                 destSubnet = subnet;
547                 break;
548             }
549         }
550         if (destSubnet == null) {
551             LOG.trace("Destination IP address does not match any subnet in tenant {}", destL3Address.getIpAddress());
552             return null;
553         }
554
555         if (destSubnet.getVirtualRouterIp() == null) {
556             LOG.trace("Destination subnet {} for Endpoint {}.{} has no gateway IP", destSubnet.getIpPrefix(),
557                     destL3Address.getKey());
558             return null;
559         }
560
561         if (srcSubnet.getVirtualRouterIp() == null) {
562             LOG.trace("Local subnet {} has no gateway IP", srcSubnet.getIpPrefix());
563             return null;
564         }
565         L3Context destL3c = getL3ContextForSubnet(destEp.getTenant(), destSubnet);
566         if (destL3c == null || destL3c.getId() == null) {
567             LOG.error("No L3 Context found associated with subnet {}", destSubnet.getId());
568             return null;
569         }
570         L3Context srcL3c = getL3ContextForSubnet(destEp.getTenant(), srcSubnet);
571         if (srcL3c == null || srcL3c.getId() == null) {
572             LOG.error("No L3 Context found associated with subnet {}", srcSubnet.getId());
573             return null;
574         }
575
576         if (!(srcL3c.getId().getValue().equals(destL3c.getId().getValue()))) {
577             LOG.trace("Trying to route between two L3Contexts {} and {}. Not currently supported.", srcL3c.getId()
578                 .getValue(), destL3c.getId().getValue());
579             return null;
580         }
581
582         MacAddress matcherMac = routerPortMac(destL3c, srcSubnet.getVirtualRouterIp());
583         MacAddress epDestMac = destEp.getMacAddress();
584         MacAddress destSubnetGatewayMac = routerPortMac(destL3c, destSubnet.getVirtualRouterIp());
585
586         if (srcSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
587             // This is our final destination, so match on actual EP mac.
588             matcherMac = epDestMac;
589         }
590
591         ArrayList<Instruction> l3instructions = new ArrayList<>();
592         List<Action> applyActions = new ArrayList<>();
593         List<Action> l3ApplyActions = new ArrayList<>();
594
595         int order = 0;
596
597         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
598         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
599         Action setNextHop;
600         String nextHop;
601
602         // BEGIN L3 LOCAL
603         nextHop = ofc.getNodeConnectorId().getValue();
604
605         long portNum;
606         try {
607             portNum = getOfPortNum(ofc.getNodeConnectorId());
608         } catch (NumberFormatException ex) {
609             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
610             return null;
611         }
612
613         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
614         // END L3 LOCAL
615
616         // Lets not re-write the srcMac if its local.
617         if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
618             Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
619             l3ApplyActions.add(setDlSrc);
620         }
621
622         Action setDlDst = setDlDstAction(epDestMac);
623         l3ApplyActions.add(setDlDst);
624
625         Action decTtl = decNwTtlAction();
626         l3ApplyActions.add(decTtl);
627
628         order += 1;
629         applyActions.add(setdEPG);
630         applyActions.add(setdCG);
631         applyActions.add(setNextHop);
632
633         applyActions.addAll(l3ApplyActions);
634         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
635             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
636             .build();
637
638         l3instructions.add(applyActionsIns);
639         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
640             .setInstruction(gotoTableIns((short) (getTableId() + 1)))
641             .build();
642         l3instructions.add(gotoTable);
643         Layer3Match m = null;
644         Long etherType = null;
645         String ikey = null;
646         if (destL3Address.getIpAddress().getIpv4Address() != null) {
647             ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
648             etherType = IPv4;
649             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
650         } else if (destL3Address.getIpAddress().getIpv6Address() != null) {
651             ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
652             etherType = IPv6;
653             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
654         } else {
655             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", destL3Address.toString());
656             return null;
657         }
658
659         FlowId flowid = new FlowId(new StringBuilder().append(Integer.toString(epFwdCtxOrds.getL3Id()))
660             .append("|l3|")
661             .append(ikey)
662             .append("|")
663             .append(Integer.toString(epFwdCtxOrds.getEpgId()))
664             .append("|")
665             .append(Integer.toString(epFwdCtxOrds.getCgId()))
666             .append("|")
667             .append(matcherMac)
668             .append("|")
669             .append(destSubnetGatewayMac)
670             .append("|")
671             .append(nextHop)
672             .toString());
673         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
674             .setLayer3Match(m);
675         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(epFwdCtxOrds.getL3Id())));
676         FlowBuilder flowb = base().setId(flowid)
677             .setPriority(Integer.valueOf(132))
678             .setMatch(mb.build())
679             .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
680         return flowb.build();
681     }
682
683     private Flow createRemoteL2Flow(Endpoint ep, NodeId nodeId, EndpointFwdCtxOrdinals srcEpFwdCtxOrds,
684             EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc) {
685
686         // TODO Li alagalah - refactor common code but keep simple method
687
688         // this endpoint is on a different switch; send to the
689         // appropriate tunnel
690
691         ArrayList<Instruction> instructions = new ArrayList<>();
692         List<Action> applyActions = new ArrayList<>();
693
694         int order = 0;
695
696         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(destEpFwdCtxOrds.getEpgId()));
697         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(destEpFwdCtxOrds.getCgId()));
698         Action setNextHop;
699         String nextHop;
700
701         // BEGIN TUNNEL HANDLING
702         IpAddress tunDst = ctx.getSwitchManager().getTunnelIP(ofc.getNodeId(), TunnelTypeVxlan.class);
703         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
704         if (tunDst == null) {
705             LOG.warn("Failed to get Tunnel IP for NodeId {} with EP {}", nodeId, ep);
706             return null;
707         }
708         if (tunPort == null) {
709             LOG.warn("Failed to get Tunnel Port for NodeId {} with EP {}", nodeId, ep);
710             return null;
711         }
712
713         Action tundstAction;
714
715         if (tunDst.getIpv4Address() != null) {
716             nextHop = tunDst.getIpv4Address().getValue();
717             tundstAction = nxLoadTunIPv4Action(nextHop, false);
718         } else if (tunDst.getIpv6Address() != null) {
719             // nextHop = tunDst.getIpv6Address().getValue();
720             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
721                     ofc.getNodeId());
722             return null;
723         } else {
724             // this shouldn't happen
725             LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
726             return null;
727         }
728
729         long portNum;
730         try {
731             portNum = getOfPortNum(tunPort);
732         } catch (NumberFormatException ex) {
733             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
734             return null;
735         }
736
737         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
738         applyActions.add(tundstAction);
739         // END TUNNEL
740
741         order += 1;
742         applyActions.add(setdEPG);
743         applyActions.add(setdCG);
744         applyActions.add(setNextHop);
745         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
746             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
747             .build();
748         instructions.add(applyActionsIns);
749
750         applyActionsIns = new InstructionBuilder().setOrder(order++)
751             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
752             .build();
753
754         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
755             .setInstruction(gotoTableIns((short) (getTableId() + 1)))
756             .build();
757         instructions.add(gotoTable);
758
759         FlowId flowid = new FlowId(new StringBuilder().append(destEpFwdCtxOrds.getBdId())
760             .append("|l2|")
761             .append(ep.getMacAddress().getValue())
762             .append("|")
763             .append(srcEpFwdCtxOrds.getBdId())
764             .append("|")
765             .append(nextHop)
766             .toString());
767         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, ep.getMacAddress(), null));
768         addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(destEpFwdCtxOrds.getBdId())));
769         FlowBuilder flowb = base().setId(flowid)
770             .setPriority(Integer.valueOf(50))
771             .setMatch(mb.build())
772             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
773
774         return flowb.build();
775     }
776
777     private Flow createRemoteL3RoutedFlow(Endpoint destEp, L3Address destL3Address, NodeId nodeId,
778             EndpointFwdCtxOrdinals srcEpFwdCtxOrds, EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc,
779             Subnet srcSubnet) {
780
781         // TODO Li alagalah - refactor common code but keep simple method
782
783         // this endpoint is on a different switch; send to the
784         // appropriate tunnel
785         Subnet destSubnet = null;
786         HashSet<Subnet> subnets = getSubnets(destEp.getTenant());
787         if (subnets == null) {
788             LOG.trace("No subnets in tenant {}", destL3Address.getIpAddress());
789             return null;
790         }
791         NetworkDomainId epNetworkContainment = getEPNetworkContainment(destEp);
792         for (Subnet subnet : subnets) {
793             // TODO Li alagalah add IPv6 support
794             if (subnet.getId().getValue().equals(epNetworkContainment.getValue())) {
795                 destSubnet = subnet;
796                 break;
797             }
798         }
799         if (destSubnet == null) {
800             LOG.info("Destination IP address does not match any subnet in tenant {}", destL3Address.getIpAddress());
801             return null;
802         }
803
804         if (destSubnet.getVirtualRouterIp() == null) {
805             LOG.trace("Destination subnet {} for Endpoint {}.{} has no gateway IP", destSubnet.getIpPrefix(),
806                     destL3Address.getKey());
807             return null;
808         }
809
810         if (srcSubnet.getVirtualRouterIp() == null) {
811             LOG.trace("Local subnet {} has no gateway IP", srcSubnet.getIpPrefix());
812             return null;
813         }
814         L3Context destL3c = getL3ContextForSubnet(destEp.getTenant(), destSubnet);
815         if (destL3c == null || destL3c.getId() == null) {
816             LOG.error("No L3 Context found associated with subnet {}", destSubnet.getId());
817             return null;
818         }
819         L3Context srcL3c = getL3ContextForSubnet(destEp.getTenant(), srcSubnet);
820         if (srcL3c == null || srcL3c.getId() == null) {
821             LOG.error("No L3 Context found associated with subnet {}", srcSubnet.getId());
822             return null;
823         }
824
825         if (!(srcL3c.getId().getValue().equals(destL3c.getId().getValue()))) {
826             LOG.trace("Trying to route between two L3Contexts {} and {}. Not currently supported.", srcL3c.getId()
827                 .getValue(), destL3c.getId().getValue());
828             return null;
829         }
830
831         MacAddress matcherMac = routerPortMac(destL3c, srcSubnet.getVirtualRouterIp());
832         MacAddress epDestMac = destEp.getMacAddress();
833         MacAddress destSubnetGatewayMac = routerPortMac(destL3c, destSubnet.getVirtualRouterIp());
834
835         ArrayList<Instruction> l3instructions = new ArrayList<>();
836         List<Action> applyActions = new ArrayList<>();
837         List<Action> l3ApplyActions = new ArrayList<>();
838
839         int order = 0;
840
841         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(destEpFwdCtxOrds.getEpgId()));
842         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(destEpFwdCtxOrds.getCgId()));
843         Action setNextHop;
844         String nextHop;
845
846         // BEGIN TUNNEL HANDLING
847         IpAddress tunDst = ctx.getSwitchManager().getTunnelIP(ofc.getNodeId(), TunnelTypeVxlan.class);
848         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
849         if (tunDst == null) {
850             LOG.warn("Failed to get Tunnel IP for NodeId {} with L3Address {}", nodeId, destL3Address);
851             return null;
852         }
853         if (tunPort == null) {
854             LOG.warn("Failed to get Tunnel port for NodeId {} with L3Address {}", nodeId, destL3Address);
855             return null;
856         }
857
858         Action tundstAction;
859
860         if (tunDst.getIpv4Address() != null) {
861             nextHop = tunDst.getIpv4Address().getValue();
862             tundstAction = nxLoadTunIPv4Action(nextHop, false);
863         } else if (tunDst.getIpv6Address() != null) {
864             // nextHop = tunDst.getIpv6Address().getValue();
865             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
866                     ofc.getNodeId());
867             return null;
868         } else {
869             // this shouldn't happen
870             LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
871             return null;
872         }
873
874         long portNum;
875         try {
876             portNum = getOfPortNum(tunPort);
877         } catch (NumberFormatException ex) {
878             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
879             return null;
880         }
881
882         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
883         applyActions.add(tundstAction);
884         // END TUNNEL
885
886         order += 1;
887         applyActions.add(setdEPG);
888         applyActions.add(setdCG);
889         applyActions.add(setNextHop);
890
891         Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
892         l3ApplyActions.add(setDlSrc);
893
894         Action setDlDst = setDlDstAction(epDestMac);
895         l3ApplyActions.add(setDlDst);
896
897         Action decTtl = decNwTtlAction();
898         l3ApplyActions.add(decTtl);
899
900         applyActions.addAll(l3ApplyActions);
901         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
902             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
903             .build();
904
905         l3instructions.add(applyActionsIns);
906         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
907             .setInstruction(gotoTableIns((short) (getTableId() + 1)))
908             .build();
909         l3instructions.add(gotoTable);
910         Layer3Match m = null;
911         Long etherType = null;
912         String ikey = null;
913         if (destL3Address.getIpAddress().getIpv4Address() != null) {
914             ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
915             etherType = IPv4;
916             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
917         } else if (destL3Address.getIpAddress().getIpv6Address() != null) {
918             ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
919             etherType = IPv6;
920             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
921         } else {
922             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", destL3Address.toString());
923             return null;
924         }
925
926         FlowId flowid = new FlowId(new StringBuilder().append(Integer.toString(destEpFwdCtxOrds.getL3Id()))
927             .append("|l3|")
928             .append(ikey)
929             .append("|")
930             .append(matcherMac)
931             .append("|")
932             .append(destSubnetGatewayMac)
933             .append("|")
934             .append(srcEpFwdCtxOrds.getL3Id())
935             .append("|")
936             .append(nextHop)
937             .toString());
938         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
939             .setLayer3Match(m);
940         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(destEpFwdCtxOrds.getL3Id())));
941
942         FlowBuilder flowb = base().setId(flowid)
943             .setPriority(Integer.valueOf(132))
944             .setMatch(mb.build())
945             .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
946         return flowb.build();
947     }
948
949     private NetworkDomainId getEPNetworkContainment(Endpoint endpoint) {
950         if (endpoint.getNetworkContainment() != null) {
951             return endpoint.getNetworkContainment();
952         } else {
953             /*
954              * TODO: Be alagalah: Endpoint Refactor: This should be set on input
955              * which we can't do because of the backwards way endpoints were
956              * "architected".
957              */
958             return ctx.getPolicyResolver()
959                 .getTenant(endpoint.getTenant())
960                 .getEndpointGroup(endpoint.getEndpointGroup())
961                 .getNetworkDomain();
962         }
963     }
964
965     private HashSet<Subnet> getSubnets(final TenantId tenantId) {
966
967         // if (subnetsByTenant.get(tenantId) != null) {
968         // return subnetsByTenant.get(tenantId);
969         // }
970
971         if (ctx.getDataBroker() == null) {
972             return null;
973         }
974
975         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
976         InstanceIdentifier<Tenant> tiid = TenantUtils.tenantIid(tenantId);
977         Optional<Tenant> tenantInfo;
978         try {
979             tenantInfo = t.read(LogicalDatastoreType.CONFIGURATION, tiid).get();
980         } catch (Exception e) {
981             LOG.error("Could not read Tenant {}", tenantId, e);
982             return null;
983         }
984
985         HashSet<Subnet> subnets = new HashSet<Subnet>();
986
987         if (!tenantInfo.isPresent()) {
988             LOG.warn("Tenant {} not found", tenantId);
989             return null;
990         }
991
992         subnets.addAll(tenantInfo.get().getSubnet());
993         // subnetsByTenant.put(tenantId, subnets);
994         return subnets;
995     }
996
997     // Need a method to get subnets for EPs attached to the node locally
998     // to set the source Mac address for the router interface.
999     private List<Subnet> getLocalSubnets(NodeId nodeId) {
1000         Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
1001
1002         List<Subnet> localSubnets = new ArrayList<Subnet>();
1003
1004         for (Endpoint endpoint : endpointsForNode) {
1005             HashSet<Subnet> subnets = getSubnets(endpoint.getTenant());
1006             if (subnets == null) {
1007                 LOG.error("No local subnets.");
1008                 return null;
1009             }
1010             NetworkDomainId epNetworkContainment = getEPNetworkContainment(endpoint);
1011             for (Subnet subnet : subnets) {
1012                 if (epNetworkContainment.getValue().equals(subnet.getId().getValue())) {
1013                     localSubnets.add(subnet);
1014                 }
1015             }
1016         }
1017         return localSubnets;
1018     }
1019
1020     /**
1021      * Reads data from datastore as synchronous call.
1022      *
1023      * @return {@link Optional#isPresent()} is {@code true} if reading was
1024      *         successful and data exists in datastore; {@link Optional#isPresent()} is
1025      *         {@code false} otherwise
1026      */
1027     public static <T extends DataObject> Optional<T> readFromDs(LogicalDatastoreType store, InstanceIdentifier<T> path,
1028             ReadTransaction rTx) {
1029         CheckedFuture<Optional<T>, ReadFailedException> resultFuture = rTx.read(store, path);
1030         try {
1031             return resultFuture.checkedGet();
1032         } catch (ReadFailedException e) {
1033             LOG.warn("Read failed from DS.", e);
1034             return Optional.absent();
1035         }
1036     }
1037
1038     static byte[] bytesFromHexString(String values) {
1039         String target = "";
1040         if (values != null) {
1041             target = values;
1042         }
1043         String[] octets = target.split(":");
1044
1045         byte[] ret = new byte[octets.length];
1046         for (int i = 0; i < octets.length; i++) {
1047             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
1048         }
1049         return ret;
1050     }
1051 }