ELAN: skip remote unicast MACs
[netvirt.git] / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / ha / WeightedCentralizedSwitchScheduler.java
1 /*
2  * Copyright (c) 2017 Red Hat, 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.netvirt.natservice.ha;
10
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.concurrent.ConcurrentHashMap;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
27 import org.opendaylight.netvirt.natservice.internal.NatUtil;
28 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 @Singleton
44 public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchScheduler {
45     private static final Logger LOG = LoggerFactory.getLogger(WeightedCentralizedSwitchScheduler.class);
46     private static final Integer INITIAL_SWITCH_WEIGHT = Integer.valueOf(0);
47
48     private final Map<BigInteger,Integer> switchWeightsMap = new ConcurrentHashMap<>();
49     private final Map<String,String> subnetIdToRouterPortMap = new ConcurrentHashMap<>();
50     private final DataBroker dataBroker;
51     private final OdlInterfaceRpcService interfaceManager;
52     private final IVpnFootprintService vpnFootprintService;
53
54     @Inject
55     public WeightedCentralizedSwitchScheduler(DataBroker dataBroker, OdlInterfaceRpcService interfaceManager,
56             IVpnFootprintService vpnFootprintService) {
57         this.dataBroker = dataBroker;
58         this.interfaceManager = interfaceManager;
59         this.vpnFootprintService = vpnFootprintService;
60     }
61
62     @Override
63     public boolean scheduleCentralizedSwitch(Routers router) {
64         BigInteger nextSwitchId = getSwitchWithLowestWeight();
65         String routerName = router.getRouterName();
66         RouterToNaptSwitchBuilder routerToNaptSwitchBuilder =
67                 new RouterToNaptSwitchBuilder().setRouterName(routerName);
68         RouterToNaptSwitch id = routerToNaptSwitchBuilder.setPrimarySwitchId(nextSwitchId).build();
69         addToDpnMaps(routerName, router.getSubnetIds(), nextSwitchId);
70         try {
71             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
72                     getNaptSwitchesIdentifier(routerName), id);
73             switchWeightsMap.put(nextSwitchId,switchWeightsMap.get(nextSwitchId) + 1);
74
75         } catch (TransactionCommitFailedException e) {
76             LOG.error("ScheduleCentralizedSwitch failed for {}", routerName);
77         }
78         return true;
79
80     }
81
82     @Override
83     public boolean updateCentralizedSwitch(Routers oldRouter, Routers newRouter) {
84         String routerName = newRouter.getRouterName();
85         List<Uuid> addedSubnetIds = getUpdatedSubnetIds(newRouter.getSubnetIds(), oldRouter.getSubnetIds());
86         List<Uuid> deletedSubnetIds = getUpdatedSubnetIds(oldRouter.getSubnetIds(), newRouter.getSubnetIds());
87         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, newRouter.getRouterName());
88         addToDpnMaps(routerName, addedSubnetIds, primarySwitchId);
89         deleteFromDpnMaps(routerName, deletedSubnetIds, primarySwitchId);
90         return true;
91     }
92
93     @Override
94     public boolean releaseCentralizedSwitch(Routers router) {
95         String routerName = router.getRouterName();
96         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
97         deleteFromDpnMaps(routerName, router.getSubnetIds(), primarySwitchId);
98         try {
99             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
100                     getNaptSwitchesIdentifier(routerName));
101             switchWeightsMap.put(primarySwitchId,switchWeightsMap.get(primarySwitchId) - 1);
102         } catch (TransactionCommitFailedException e) {
103             return false;
104         }
105         return true;
106     }
107
108     private void addToDpnMaps(String routerName, List<Uuid> addedSubnetIds, BigInteger primarySwitchId) {
109         if (addedSubnetIds == null || addedSubnetIds.isEmpty()) {
110             LOG.debug("addToDpnMaps no subnets associated with {}", routerName);
111             return;
112         }
113         String primaryRd = NatUtil.getPrimaryRd(dataBroker, routerName);
114         WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
115         for (Uuid subnetUuid : addedSubnetIds) {
116             try {
117                 Subnetmap subnetMapEntry = SingleTransactionDataBroker.syncRead(dataBroker,
118                         LogicalDatastoreType.CONFIGURATION, getSubnetMapIdentifier(subnetUuid));
119                 Uuid routerPortUuid = subnetMapEntry.getRouterInterfacePortId();
120                 subnetIdToRouterPortMap.put(subnetUuid.getValue(), routerPortUuid.getValue());
121                 vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
122                         routerPortUuid.getValue(), null, true);
123                 NatUtil.addToNeutronRouterDpnsMap(dataBroker, routerName, routerPortUuid.getValue(),
124                         primarySwitchId, writeOperTxn);
125                 NatUtil.addToDpnRoutersMap(dataBroker, routerName, routerPortUuid.getValue(),
126                         primarySwitchId, writeOperTxn);
127             } catch (ReadFailedException e) {
128                 LOG.error("addToDpnMaps failed for {}", routerName);
129             }
130         }
131         writeOperTxn.submit();
132     }
133
134
135
136     private void deleteFromDpnMaps(String routerName, List<Uuid> deletedSubnetIds, BigInteger primarySwitchId) {
137         if (deletedSubnetIds == null || deletedSubnetIds.isEmpty()) {
138             LOG.debug("deleteFromDpnMaps no subnets associated with {}", routerName);
139             return;
140         }
141         WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
142         String primaryRd = NatUtil.getPrimaryRd(dataBroker, routerName);
143         for (Uuid subnetUuid :deletedSubnetIds) {
144             String routerPort = subnetIdToRouterPortMap.remove(subnetUuid.getValue());
145             if (routerPort == null) {
146                 LOG.error("The router port was not found for {}", subnetUuid.getValue());
147                 continue;
148             }
149             vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
150                     routerPort, null, false);
151             NatUtil.removeFromNeutronRouterDpnsMap(dataBroker, routerName, primarySwitchId, writeOperTxn);
152             NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, routerName, interfaceManager,
153                     writeOperTxn);
154         }
155         writeOperTxn.submit();
156     }
157
158     @Override
159     public boolean addSwitch(BigInteger dpnId) {
160         /* Initialize the switch in the map with weight 0 */
161         LOG.info("addSwitch: Adding {} dpnId to switchWeightsMap", dpnId);
162         switchWeightsMap.put(dpnId, INITIAL_SWITCH_WEIGHT);
163         return true;
164
165     }
166
167     @Override
168     public boolean removeSwitch(BigInteger dpnId) {
169         LOG.info("removeSwitch: Removing {} dpnId to switchWeightsMap", dpnId);
170         if (!INITIAL_SWITCH_WEIGHT.equals(switchWeightsMap.get(dpnId))) {
171             NaptSwitches naptSwitches = getNaptSwitches(dataBroker);
172             for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
173                 if (dpnId.equals(routerToNaptSwitch.getPrimarySwitchId())) {
174                     Routers router = NatUtil.getRoutersFromConfigDS(dataBroker, routerToNaptSwitch.getRouterName());
175                     releaseCentralizedSwitch(router);
176                     switchWeightsMap.remove(dpnId);
177                     scheduleCentralizedSwitch(router);
178                     break;
179                 }
180             }
181         } else {
182             switchWeightsMap.remove(dpnId);
183         }
184         return true;
185     }
186
187     public static NaptSwitches getNaptSwitches(DataBroker dataBroker) {
188         InstanceIdentifier<NaptSwitches> id = InstanceIdentifier.builder(NaptSwitches.class).build();
189         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
190                 LogicalDatastoreType.CONFIGURATION, id).orNull();
191     }
192
193     private BigInteger getSwitchWithLowestWeight() {
194         int lowestWeight = Integer.MAX_VALUE;
195         BigInteger nextSwitchId = BigInteger.valueOf(0);
196         for (Entry<BigInteger, Integer> entry : switchWeightsMap.entrySet()) {
197             BigInteger dpnId = entry.getKey();
198             Integer weight = entry.getValue();
199             if (lowestWeight > weight) {
200                 lowestWeight = weight;
201                 nextSwitchId =  dpnId;
202             }
203         }
204         return nextSwitchId;
205     }
206
207     private InstanceIdentifier<RouterToNaptSwitch> getNaptSwitchesIdentifier(String routerName) {
208         return InstanceIdentifier.builder(NaptSwitches.class)
209             .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
210     }
211
212     private InstanceIdentifier<Subnetmap> getSubnetMapIdentifier(Uuid subnetId) {
213         return InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
214                 new SubnetmapKey(subnetId)).build();
215     }
216
217     @Override
218     public boolean getCentralizedSwitch(String routerName) {
219         // TODO Auto-generated method stub
220         return false;
221     }
222
223     public static List<Uuid> getUpdatedSubnetIds(
224             List<Uuid> updatedSubnetIds,
225             List<Uuid> currentSubnetIds) {
226         if (updatedSubnetIds == null) {
227             return null;
228         }
229         List<Uuid> newSubnetIds = new ArrayList<>(updatedSubnetIds);
230         if (currentSubnetIds == null) {
231             return newSubnetIds;
232         }
233         List<Uuid> origSubnetIds = new ArrayList<>(currentSubnetIds);
234         for (Iterator<Uuid> iterator = newSubnetIds.iterator(); iterator.hasNext();) {
235             Uuid updatedSubnetId = iterator.next();
236             for (Uuid currentSubnetId : origSubnetIds) {
237                 if (updatedSubnetId.getValue().equals(currentSubnetId.getValue())) {
238                     iterator.remove();
239                     break;
240                 }
241             }
242         }
243         return newSubnetIds;
244     }
245 }