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