2 * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.natservice.ha;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
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;
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);
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;
55 public WeightedCentralizedSwitchScheduler(DataBroker dataBroker, OdlInterfaceRpcService interfaceManager,
56 IVpnFootprintService vpnFootprintService) {
57 this.dataBroker = dataBroker;
58 this.interfaceManager = interfaceManager;
59 this.vpnFootprintService = vpnFootprintService;
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);
71 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
72 getNaptSwitchesIdentifier(routerName), id);
73 switchWeightsMap.put(nextSwitchId,switchWeightsMap.get(nextSwitchId) + 1);
75 } catch (TransactionCommitFailedException e) {
76 LOG.error("ScheduleCentralizedSwitch failed for {}", routerName);
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);
94 public boolean releaseCentralizedSwitch(Routers router) {
95 String routerName = router.getRouterName();
96 BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
97 deleteFromDpnMaps(routerName, router.getSubnetIds(), primarySwitchId);
99 SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
100 getNaptSwitchesIdentifier(routerName));
101 switchWeightsMap.put(primarySwitchId,switchWeightsMap.get(primarySwitchId) - 1);
102 } catch (TransactionCommitFailedException e) {
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);
113 String primaryRd = NatUtil.getPrimaryRd(dataBroker, routerName);
114 WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
115 for (Uuid subnetUuid : addedSubnetIds) {
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);
131 writeOperTxn.submit();
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);
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());
149 vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
150 routerPort, null, false);
151 NatUtil.removeFromNeutronRouterDpnsMap(dataBroker, routerName, primarySwitchId, writeOperTxn);
152 NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, routerName, interfaceManager,
155 writeOperTxn.submit();
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);
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);
182 switchWeightsMap.remove(dpnId);
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();
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;
207 private InstanceIdentifier<RouterToNaptSwitch> getNaptSwitchesIdentifier(String routerName) {
208 return InstanceIdentifier.builder(NaptSwitches.class)
209 .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
212 private InstanceIdentifier<Subnetmap> getSubnetMapIdentifier(Uuid subnetId) {
213 return InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
214 new SubnetmapKey(subnetId)).build();
218 public boolean getCentralizedSwitch(String routerName) {
219 // TODO Auto-generated method stub
223 public static List<Uuid> getUpdatedSubnetIds(
224 List<Uuid> updatedSubnetIds,
225 List<Uuid> currentSubnetIds) {
226 if (updatedSubnetIds == null) {
229 List<Uuid> newSubnetIds = new ArrayList<>(updatedSubnetIds);
230 if (currentSubnetIds == null) {
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())) {