Bug 9299 - In conntrack SNAT Vpn to dpn maps fails to update when a
[netvirt.git] / vpnservice / 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.concurrent.ConcurrentHashMap;
17
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 final Map<BigInteger,Integer> switchWeightsMap = new ConcurrentHashMap<>();
47     private final Map<String,String> subnetIdToRouterPortMap = new ConcurrentHashMap<>();
48     private final DataBroker dataBroker;
49     final OdlInterfaceRpcService interfaceManager;
50     private final int initialSwitchWeight = 0;
51     private final IVpnFootprintService vpnFootprintService;
52
53     @Inject
54     public WeightedCentralizedSwitchScheduler(DataBroker dataBroker, OdlInterfaceRpcService interfaceManager,
55             IVpnFootprintService vpnFootprintService) {
56         this.dataBroker = dataBroker;
57         this.interfaceManager = interfaceManager;
58         this.vpnFootprintService = vpnFootprintService;
59     }
60
61     @Override
62     public boolean scheduleCentralizedSwitch(Routers router) {
63         BigInteger nextSwitchId = getSwitchWithLowestWeight();
64         String routerName = router.getRouterName();
65         RouterToNaptSwitchBuilder routerToNaptSwitchBuilder =
66                 new RouterToNaptSwitchBuilder().setRouterName(routerName);
67         RouterToNaptSwitch id = routerToNaptSwitchBuilder.setPrimarySwitchId(nextSwitchId).build();
68         addToDpnMaps(routerName, router.getSubnetIds(), nextSwitchId);
69         try {
70             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
71                     getNaptSwitchesIdentifier(routerName), id);
72             switchWeightsMap.put(nextSwitchId,switchWeightsMap.get(nextSwitchId) + 1);
73
74         } catch (TransactionCommitFailedException e) {
75             LOG.error("ScheduleCentralizedSwitch failed for {}", routerName);
76         }
77         return true;
78
79     }
80
81     @Override
82     public boolean updateCentralizedSwitch(Routers oldRouter, Routers newRouter) {
83         String routerName = newRouter.getRouterName();
84         List<Uuid> addedSubnetIds = getUpdatedSubnetIds(newRouter.getSubnetIds(), oldRouter.getSubnetIds());
85         List<Uuid> deletedSubnetIds = getUpdatedSubnetIds(oldRouter.getSubnetIds(), newRouter.getSubnetIds());
86         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, newRouter.getRouterName());
87         addToDpnMaps(routerName, addedSubnetIds, primarySwitchId);
88         deleteFromDpnMaps(routerName, deletedSubnetIds, primarySwitchId);
89         return true;
90     }
91
92     @Override
93     public boolean releaseCentralizedSwitch(Routers router) {
94         String routerName = router.getRouterName();
95         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
96         deleteFromDpnMaps(routerName, router.getSubnetIds(), primarySwitchId);
97         try {
98             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
99                     getNaptSwitchesIdentifier(routerName));
100             switchWeightsMap.put(primarySwitchId,switchWeightsMap.get(primarySwitchId) - 1);
101         } catch (TransactionCommitFailedException e) {
102             return false;
103         }
104         return true;
105     }
106
107     private void addToDpnMaps(String routerName, List<Uuid> addedSubnetIds, BigInteger primarySwitchId) {
108         if (addedSubnetIds == null || addedSubnetIds.isEmpty()) {
109             LOG.debug("addToDpnMaps no subnets associated with {}", routerName);
110             return;
111         }
112         String primaryRd = NatUtil.getPrimaryRd(dataBroker, routerName);
113         WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
114         for (Uuid subnetUuid : addedSubnetIds) {
115             try {
116                 Subnetmap subnetMapEntry = SingleTransactionDataBroker.syncRead(dataBroker,
117                         LogicalDatastoreType.CONFIGURATION, getSubnetMapIdentifier(subnetUuid));
118                 Uuid routerPortUuid = subnetMapEntry.getRouterInterfacePortId();
119                 subnetIdToRouterPortMap.put(subnetUuid.getValue(), routerPortUuid.getValue());
120                 vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
121                         routerPortUuid.getValue(), null, true);
122                 NatUtil.addToNeutronRouterDpnsMap(dataBroker, routerName, routerPortUuid.getValue(),
123                         primarySwitchId, writeOperTxn);
124                 NatUtil.addToDpnRoutersMap(dataBroker, routerName, routerPortUuid.getValue(),
125                         primarySwitchId, writeOperTxn);
126             } catch (ReadFailedException e) {
127                 LOG.error("addToDpnMaps failed for {}", routerName);
128             }
129         }
130         writeOperTxn.submit();
131     }
132
133
134
135     private void deleteFromDpnMaps(String routerName, List<Uuid> deletedSubnetIds, BigInteger primarySwitchId) {
136         if (deletedSubnetIds == null || deletedSubnetIds.isEmpty()) {
137             LOG.debug("deleteFromDpnMaps no subnets associated with {}", routerName);
138             return;
139         }
140         WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
141         String primaryRd = NatUtil.getPrimaryRd(dataBroker, routerName);
142         for (Uuid subnetUuid :deletedSubnetIds) {
143             String routerPort = subnetIdToRouterPortMap.remove(subnetUuid.getValue());
144             if (routerPort == null) {
145                 LOG.error("The router port was not found for {}", subnetUuid.getValue());
146                 continue;
147             }
148             vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
149                     routerPort, null, false);
150             NatUtil.removeFromNeutronRouterDpnsMap(dataBroker, routerName, primarySwitchId, writeOperTxn);
151             NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, routerName, interfaceManager,
152                     writeOperTxn);
153         }
154         writeOperTxn.submit();
155     }
156
157     @Override
158     public boolean addSwitch(BigInteger dpnId) {
159         /* Initialize the switch in the map with weight 0 */
160         switchWeightsMap.put(dpnId, initialSwitchWeight);
161         return true;
162
163     }
164
165     @Override
166     public boolean removeSwitch(BigInteger dpnId) {
167         if (switchWeightsMap.get(dpnId) != initialSwitchWeight) {
168             NaptSwitches naptSwitches = getNaptSwitches(dataBroker);
169             for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
170                 if (dpnId.equals(routerToNaptSwitch.getPrimarySwitchId())) {
171                     Routers router = NatUtil.getRoutersFromConfigDS(dataBroker, routerToNaptSwitch.getRouterName());
172                     releaseCentralizedSwitch(router);
173                     switchWeightsMap.remove(dpnId);
174                     scheduleCentralizedSwitch(router);
175                     break;
176                 }
177             }
178         } else {
179             switchWeightsMap.remove(dpnId);
180         }
181         return true;
182     }
183
184     public static NaptSwitches getNaptSwitches(DataBroker dataBroker) {
185         InstanceIdentifier<NaptSwitches> id = InstanceIdentifier.builder(NaptSwitches.class).build();
186         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
187                 LogicalDatastoreType.CONFIGURATION, id).orNull();
188     }
189
190     private BigInteger getSwitchWithLowestWeight() {
191         int lowestWeight = Integer.MAX_VALUE;
192         BigInteger nextSwitchId = BigInteger.valueOf(0);
193         for (BigInteger dpnId : switchWeightsMap.keySet()) {
194             if (lowestWeight > switchWeightsMap.get(dpnId)) {
195                 lowestWeight = switchWeightsMap.get(dpnId);
196                 nextSwitchId =  dpnId;
197             }
198         }
199         return nextSwitchId;
200     }
201
202     private InstanceIdentifier<RouterToNaptSwitch> getNaptSwitchesIdentifier(String routerName) {
203         return InstanceIdentifier.builder(NaptSwitches.class)
204             .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
205     }
206
207     private InstanceIdentifier<Subnetmap> getSubnetMapIdentifier(Uuid subnetId) {
208         return InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
209                 new SubnetmapKey(subnetId)).build();
210     }
211
212     @Override
213     public boolean getCentralizedSwitch(String routerName) {
214         // TODO Auto-generated method stub
215         return false;
216     }
217
218     public static List<Uuid> getUpdatedSubnetIds(
219             List<Uuid> updatedSubnetIds,
220             List<Uuid> currentSubnetIds) {
221         if (updatedSubnetIds == null) {
222             return null;
223         }
224         List<Uuid> newSubnetIds = new ArrayList<>(updatedSubnetIds);
225         if (currentSubnetIds == null) {
226             return newSubnetIds;
227         }
228         List<Uuid> origSubnetIds = new ArrayList<>(currentSubnetIds);
229         for (Iterator<Uuid> iterator = newSubnetIds.iterator(); iterator.hasNext();) {
230             Uuid updatedSubnetId = iterator.next();
231             for (Uuid currentSubnetId : origSubnetIds) {
232                 if (updatedSubnetId.getValue().equals(currentSubnetId.getValue())) {
233                     iterator.remove();
234                     break;
235                 }
236             }
237         }
238         return newSubnetIds;
239     }
240 }