Retain NAPT Switch after Upgrade for SNAT
[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
15 import java.math.BigInteger;
16 import java.util.Collections;
17 import java.util.List;
18 import javax.annotation.Nonnull;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
27 import org.opendaylight.genius.infra.Datastore.Configuration;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
30 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
31 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
32 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
33 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
34 import org.opendaylight.serviceutils.tools.mdsal.listener.AbstractClusteredSyncDataTreeChangeListener;
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.idmanager.rev160406.IdManagerService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExtRouters;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsalutil.rev170830.Config;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 @Singleton
50 public class UpgradeStateListener extends AbstractClusteredSyncDataTreeChangeListener<Config> {
51     private static final Logger LOG = LoggerFactory.getLogger(UpgradeStateListener.class);
52
53     private final DataBroker dataBroker;
54     private final CentralizedSwitchScheduler centralizedSwitchScheduler;
55     private final NatserviceConfig.NatMode natMode;
56     private SNATDefaultRouteProgrammer defaultRouteProgrammer;
57     private IMdsalApiManager mdsalManager;
58     private IdManagerService idManager;
59     private NaptSwitchHA naptSwitchHA;
60     private final JobCoordinator coordinator;
61     private final ManagedNewTransactionRunner txRunner;
62
63     @Inject
64     public UpgradeStateListener(final DataBroker dataBroker,
65                                 final CentralizedSwitchScheduler centralizedSwitchScheduler,
66                                 final SNATDefaultRouteProgrammer defaultRouteProgrammer,
67                                 final IMdsalApiManager mdsalManager,
68                                 final IdManagerService idManager,
69                                 final NaptSwitchHA naptSwitchHA,
70                                 final NatserviceConfig config, final JobCoordinator coordinator) {
71         super(dataBroker, new DataTreeIdentifier<>(
72                 LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Config.class)));
73         this.dataBroker = dataBroker;
74         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
75         this.centralizedSwitchScheduler = centralizedSwitchScheduler;
76         this.defaultRouteProgrammer = defaultRouteProgrammer;
77         this.coordinator = coordinator;
78         this.naptSwitchHA = naptSwitchHA;
79         if (config != null) {
80             this.natMode = config.getNatMode();
81         } else {
82             this.natMode = NatserviceConfig.NatMode.Controller;
83         }
84         LOG.trace("UpgradeStateListener (nat) initialized");
85     }
86
87     @Override
88     public void add(@Nonnull Config newDataObject) {
89     }
90
91     @Override
92     public void remove(@Nonnull Config removedDataObject) {
93         if (natMode == NatserviceConfig.NatMode.Conntrack) {
94             return;
95         }
96         LOG.debug("Verify is all Elected Napt Switch and connected back post upgrade");
97     }
98
99     @Override
100     public void update(@Nonnull Config original, Config updated) {
101         if (natMode == NatserviceConfig.NatMode.Controller) {
102             if (original.isUpgradeInProgress() && !updated.isUpgradeInProgress()) {
103                 Optional<NaptSwitches> npatSwitches = NatUtil.getAllPrimaryNaptSwitches(dataBroker);
104                 if (npatSwitches.isPresent()) {
105                     for (RouterToNaptSwitch routerToNaptSwitch : npatSwitches.get().getRouterToNaptSwitch()) {
106                         BigInteger primaryNaptDpnId = routerToNaptSwitch.getPrimarySwitchId();
107                         if (!NatUtil.getSwitchStatus(dataBroker, routerToNaptSwitch.getPrimarySwitchId())) {
108                             String routerUuid = routerToNaptSwitch.getRouterName();
109                             coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + routerUuid),
110                                 () -> Collections.singletonList(
111                                     txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
112                                         reElectNewNaptSwitch(routerUuid, primaryNaptDpnId, confTx);
113                                     }
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.getRouters()) {
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) {
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, 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 }