Scale-in/Scale-out for NAT.
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / UpgradeStateListener.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.internal;
10
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12
13 import com.google.common.base.Optional;
14 import java.math.BigInteger;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
30 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
31 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
32 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
33 import org.opendaylight.serviceutils.tools.mdsal.listener.AbstractClusteredSyncDataTreeChangeListener;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExtRouters;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.serviceutils.upgrade.rev180702.UpgradeConfig;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 @Singleton
49 public class UpgradeStateListener extends AbstractClusteredSyncDataTreeChangeListener<UpgradeConfig> {
50     private static final Logger LOG = LoggerFactory.getLogger(UpgradeStateListener.class);
51
52     private final DataBroker dataBroker;
53     private final CentralizedSwitchScheduler centralizedSwitchScheduler;
54     private final NatserviceConfig.NatMode natMode;
55     private final SNATDefaultRouteProgrammer defaultRouteProgrammer;
56     private IMdsalApiManager mdsalManager;
57     private IdManagerService idManager;
58     private final NaptSwitchHA naptSwitchHA;
59     private final JobCoordinator coordinator;
60     private final ManagedNewTransactionRunner txRunner;
61
62     @Inject
63     public UpgradeStateListener(final DataBroker dataBroker,
64                                 final CentralizedSwitchScheduler centralizedSwitchScheduler,
65                                 final SNATDefaultRouteProgrammer defaultRouteProgrammer,
66                                 final IMdsalApiManager mdsalManager,
67                                 final IdManagerService idManager,
68                                 final NaptSwitchHA naptSwitchHA,
69                                 final NatserviceConfig config, final JobCoordinator coordinator) {
70         super(dataBroker, new DataTreeIdentifier<>(
71                 LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(UpgradeConfig.class)));
72         this.dataBroker = dataBroker;
73         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
74         this.centralizedSwitchScheduler = centralizedSwitchScheduler;
75         this.defaultRouteProgrammer = defaultRouteProgrammer;
76         this.mdsalManager = mdsalManager;
77         this.idManager = idManager;
78         this.coordinator = coordinator;
79         this.naptSwitchHA = naptSwitchHA;
80         if (config != null) {
81             this.natMode = config.getNatMode();
82         } else {
83             this.natMode = NatserviceConfig.NatMode.Controller;
84         }
85         LOG.trace("UpgradeStateListener (nat) initialized");
86     }
87
88     @Override
89     public void add(@NonNull UpgradeConfig newDataObject) {
90     }
91
92     @Override
93     public void remove(@NonNull UpgradeConfig removedDataObject) {
94         if (natMode == NatserviceConfig.NatMode.Conntrack) {
95             return;
96         }
97         LOG.debug("Verify is all Elected Napt Switch and connected back post upgrade");
98     }
99
100     @Override
101     public void update(@NonNull UpgradeConfig original, UpgradeConfig updated) {
102         if (natMode == NatserviceConfig.NatMode.Controller) {
103             if (original.isUpgradeInProgress() && !updated.isUpgradeInProgress()) {
104                 Optional<NaptSwitches> npatSwitches = NatUtil.getAllPrimaryNaptSwitches(dataBroker);
105                 if (npatSwitches.isPresent()) {
106                     for (RouterToNaptSwitch routerToNaptSwitch : npatSwitches.get().nonnullRouterToNaptSwitch()) {
107                         BigInteger primaryNaptDpnId = routerToNaptSwitch.getPrimarySwitchId();
108                         if (!NatUtil.getSwitchStatus(dataBroker, routerToNaptSwitch.getPrimarySwitchId())) {
109                             String routerUuid = routerToNaptSwitch.getRouterName();
110                             coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + routerUuid,
111                                 () -> Collections.singletonList(
112                                     txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
113                                         confTx -> reElectNewNaptSwitch(routerUuid, primaryNaptDpnId, confTx)
114                                 )), NatConstants.NAT_DJC_MAX_RETRIES);
115                         }
116                     }
117                 }
118             }
119             return;
120         }
121
122         LOG.info("UpgradeStateListener update from {} to {}", original, updated);
123         if (!(original.isUpgradeInProgress() && !updated.isUpgradeInProgress())) {
124             return;
125         }
126
127         SingleTransactionDataBroker reader = new SingleTransactionDataBroker(dataBroker);
128         ExtRouters routers;
129         try {
130             routers = reader.syncRead(LogicalDatastoreType.CONFIGURATION,
131                     InstanceIdentifier.create(ExtRouters.class));
132         } catch (ReadFailedException e) {
133             LOG.error("Error reading external routers", e);
134             return;
135         }
136
137         for (Routers router : routers.nonnullRouters()) {
138             List<ExternalIps> externalIps = router.getExternalIps();
139             if (router.isEnableSnat() && externalIps != null && !externalIps.isEmpty()) {
140                 centralizedSwitchScheduler.scheduleCentralizedSwitch(router);
141             }
142         }
143     }
144
145     private void reElectNewNaptSwitch(String routerName, BigInteger primaryNaptDpnId,
146             TypedReadWriteTransaction<Configuration> confTx) throws ExecutionException, InterruptedException {
147         // Check if this is externalRouter else ignore
148         InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
149         Optional<Routers> routerData =
150                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
151                         LogicalDatastoreType.CONFIGURATION, extRoutersId);
152         if (!routerData.isPresent()) {
153             LOG.debug("reElectNewNaptSwitch : SNAT->Ignoring Re-election for router {} since its not External Router",
154                     routerName);
155             return;
156         }
157         Uuid networkId = routerData.get().getNetworkId();
158         if (networkId == null) {
159             LOG.error("hndlTepDelForSnatInEachRtr : SNAT -> Ignoring Re-election  with Napt {} for router {}"
160                     + "as external network configuraton is missing", primaryNaptDpnId, routerName);
161             return;
162         }
163         long routerId = NatUtil.getVpnId(dataBroker, routerName);
164         LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}", routerId, networkId);
165         Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
166         Long bgpVpnId;
167         if (bgpVpnUuid == null) {
168             LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
169                     routerId, routerName);
170             bgpVpnId = routerId;
171         } else {
172             bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
173             if (bgpVpnId == NatConstants.INVALID_ID) {
174                 LOG.error("hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
175                         routerName);
176                 return;
177             }
178         }
179         defaultRouteProgrammer.removeDefNATRouteInDPN(primaryNaptDpnId, bgpVpnId, confTx);
180         if (routerData.get().isEnableSnat()) {
181             LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
182
183             long routerVpnId = routerId;
184             if (bgpVpnId != NatConstants.INVALID_ID) {
185                 LOG.debug("hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
186                         + "associated to the router {}", bgpVpnId, routerName);
187                 routerVpnId = bgpVpnId;
188             } else {
189                 LOG.debug("hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
190                         + "associated to the router {}", routerVpnId, routerName);
191             }
192             //Re-elect the other available switch as the NAPT switch and program the NAT flows.
193             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
194                     routerName, networkId);
195             if (extNwProvType == null) {
196                 return;
197             }
198             NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, primaryNaptDpnId, routerName,
199                     routerId, routerVpnId, networkId, extNwProvType, confTx);
200
201         } else {
202             LOG.info("hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",
203                     routerId, primaryNaptDpnId);
204         }
205     }
206
207 }