Fix race condition between SNAT group & flow
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / ExternalNetworkGroupInstaller.java
1 /*
2  * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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 com.google.common.base.Strings;
12
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
19 import javax.annotation.Nullable;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
24 import org.opendaylight.genius.mdsalutil.ActionInfo;
25 import org.opendaylight.genius.mdsalutil.BucketInfo;
26 import org.opendaylight.genius.mdsalutil.GroupEntity;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
29 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
30 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
31 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
32 import org.opendaylight.netvirt.elanmanager.api.IElanService;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
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.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 @Singleton
44 public class ExternalNetworkGroupInstaller {
45     private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkGroupInstaller.class);
46     private final DataBroker broker;
47     private final IMdsalApiManager mdsalManager;
48     private final IElanService elanService;
49     private final IdManagerService idManager;
50     private final OdlInterfaceRpcService odlInterfaceRpcService;
51     private final JobCoordinator coordinator;
52     private final ItmRpcService itmRpcService;
53     private final IInterfaceManager interfaceManager;
54     private final NatServiceCounters natServiceCounters;
55
56     @Inject
57     public ExternalNetworkGroupInstaller(final DataBroker broker, final IMdsalApiManager mdsalManager,
58                                          final IElanService elanService, final IdManagerService idManager,
59                                          final OdlInterfaceRpcService odlInterfaceRpcService,
60                                          final JobCoordinator coordinator, final ItmRpcService itmRpcService,
61                                          final IInterfaceManager interfaceManager,
62                                          NatServiceCounters natServiceCounters) {
63
64         this.broker = broker;
65         this.mdsalManager = mdsalManager;
66         this.elanService = elanService;
67         this.idManager = idManager;
68         this.odlInterfaceRpcService = odlInterfaceRpcService;
69         this.coordinator = coordinator;
70         this.itmRpcService = itmRpcService;
71         this.interfaceManager = interfaceManager;
72         this.natServiceCounters = natServiceCounters;
73     }
74
75     public void installExtNetGroupEntries(Subnetmap subnetMap) {
76         if (subnetMap == null) {
77             LOG.error("installExtNetGroupEntries : Subnetmap is null");
78             return;
79         }
80
81         if (NatUtil.isIPv6Subnet(subnetMap.getSubnetIp())) {
82             LOG.debug("installExtNetGroupEntries : Subnet id {} is not an IPv4 subnet, hence skipping.",
83                     subnetMap.getId());
84             return;
85         }
86
87         Uuid networkId = subnetMap.getNetworkId();
88         Uuid subnetId = subnetMap.getId();
89         if (networkId == null) {
90             LOG.error("installExtNetGroupEntries : No network associated subnet id {}", subnetId.getValue());
91             return;
92         }
93
94         String macAddress = NatUtil.getSubnetGwMac(broker, subnetId, networkId.getValue());
95         installExtNetGroupEntries(subnetMap, macAddress);
96     }
97
98     public void installExtNetGroupEntries(Uuid subnetId, String macAddress) {
99         Subnetmap subnetMap = NatUtil.getSubnetMap(broker, subnetId);
100         if (subnetMap == null) {
101             LOG.error("installExtNetGroupEntries : Subnetmap is null");
102             return;
103         }
104
105         if (NatUtil.isIPv6Subnet(subnetMap.getSubnetIp())) {
106             LOG.debug("installExtNetGroupEntries : Subnet-id {} is not an IPv4 subnet, hence skipping.",
107                     subnetMap.getId());
108             return;
109         }
110         installExtNetGroupEntries(subnetMap, macAddress);
111     }
112
113     public void installExtNetGroupEntries(Uuid networkId, BigInteger dpnId) {
114         if (networkId == null) {
115             return;
116         }
117
118         List<Uuid> subnetIds = NatUtil.getSubnetIdsFromNetworkId(broker, networkId);
119         if (subnetIds.isEmpty()) {
120             LOG.error("installExtNetGroupEntries : No subnet ids associated network id {}", networkId.getValue());
121             return;
122         }
123
124         for (Uuid subnetId : subnetIds) {
125             String macAddress = NatUtil.getSubnetGwMac(broker, subnetId, networkId.getValue());
126             installExtNetGroupEntry(networkId, subnetId, dpnId, macAddress);
127         }
128     }
129
130     private void installExtNetGroupEntries(Subnetmap subnetMap, String macAddress) {
131
132         String subnetName = subnetMap.getId().getValue();
133         Uuid networkId = subnetMap.getNetworkId();
134         if (networkId == null) {
135             LOG.error("installExtNetGroupEntries : No network associated subnet id {}", subnetName);
136             return;
137         }
138
139         Collection<String> extInterfaces = elanService.getExternalElanInterfaces(networkId.getValue());
140         if (extInterfaces == null || extInterfaces.isEmpty()) {
141             LOG.trace("installExtNetGroupEntries : No external ELAN interfaces attached to network:{},subnet {}",
142                     networkId, subnetName);
143             return;
144         }
145
146         long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(subnetName), idManager);
147
148         LOG.info("installExtNetGroupEntries : Installing ext-net group {} entry for subnet {} with macAddress {} "
149                 + "(extInterfaces: {})", groupId, subnetName, macAddress, Arrays.toString(extInterfaces.toArray()));
150         for (String extInterface : extInterfaces) {
151             BigInteger dpId = NatUtil.getDpnForInterface(odlInterfaceRpcService, extInterface);
152             if (BigInteger.ZERO.equals(dpId)) {
153                 LOG.info("installExtNetGroupEntries: No DPN for interface {}. NAT ext-net flow will not be installed "
154                     + "for subnet {}", extInterface, subnetName);
155                 return;
156             }
157             installExtNetGroupEntry(groupId, subnetName, extInterface, macAddress, dpId);
158         }
159     }
160
161     public void installExtNetGroupEntry(Uuid networkId, Uuid subnetId, BigInteger dpnId, String macAddress) {
162         String subnetName = subnetId.getValue();
163         String extInterface = elanService.getExternalElanInterface(networkId.getValue(), dpnId);
164         if (extInterface == null) {
165             LOG.warn("installExtNetGroupEntry : No external ELAN interface attached to network {} subnet {} DPN id {}",
166                     networkId, subnetName, dpnId);
167             //return;
168         }
169
170         long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(subnetName), idManager);
171         LOG.info("installExtNetGroupEntry : Installing ext-net group {} entry for subnet {} with macAddress {} "
172                 + "(extInterface: {})", groupId, subnetName, macAddress, extInterface);
173         installExtNetGroupEntry(groupId, subnetName, extInterface, macAddress, dpnId);
174     }
175
176     private void installExtNetGroupEntry(long groupId, String subnetName, String extInterface,
177             String macAddress, BigInteger dpnId) {
178
179         coordinator.enqueueJob(NatUtil.getDefaultFibRouteToSNATForSubnetJobKey(subnetName, dpnId), () -> {
180             GroupEntity groupEntity = buildExtNetGroupEntity(macAddress, subnetName, groupId, extInterface, dpnId);
181             mdsalManager.syncInstallGroup(groupEntity);
182             return Collections.emptyList();
183         });
184     }
185
186     public void removeExtNetGroupEntries(Subnetmap subnetMap) {
187         if (subnetMap == null) {
188             return;
189         }
190
191         String subnetName = subnetMap.getId().getValue();
192         Uuid networkId = subnetMap.getNetworkId();
193         if (networkId == null) {
194             LOG.error("removeExtNetGroupEntries : No external network associated subnet id {}", subnetName);
195             return;
196         }
197
198         Collection<String> extInterfaces = elanService.getExternalElanInterfaces(networkId.getValue());
199         if (extInterfaces == null || extInterfaces.isEmpty()) {
200             LOG.debug("removeExtNetGroupEntries : No external ELAN interfaces attached to network {} subnet {}",
201                     networkId, subnetName);
202             return;
203         }
204
205         long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(subnetName), idManager);
206
207         for (String extInterface : extInterfaces) {
208             GroupEntity groupEntity = buildEmptyExtNetGroupEntity(subnetName, groupId, extInterface);
209             if (groupEntity != null) {
210                 LOG.info("removeExtNetGroupEntries : Remove ext-net Group: id {}, subnet id {}", groupId, subnetName);
211                 natServiceCounters.removeExternalNetworkGroup();
212                 mdsalManager.syncRemoveGroup(groupEntity);
213             }
214         }
215     }
216
217     private GroupEntity buildExtNetGroupEntity(String macAddress, String subnetName,
218                                                long groupId, String extInterface, BigInteger dpnId) {
219
220         List<ActionInfo> actionList = new ArrayList<>();
221         final int setFieldEthDestActionPos = 0;
222         List<ActionInfo> egressActionList = new ArrayList<>();
223         if (extInterface != null) {
224             egressActionList = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmRpcService,
225                     interfaceManager, extInterface, null, setFieldEthDestActionPos + 1, false);
226         }
227         if (Strings.isNullOrEmpty(macAddress) || egressActionList.isEmpty()) {
228             if (Strings.isNullOrEmpty(macAddress)) {
229                 LOG.trace("buildExtNetGroupEntity : Building ext-net group {} entry with drop action since "
230                         + "GW mac has not been resolved for subnet {} extInterface {}",
231                         groupId, subnetName, extInterface);
232             } else {
233                 LOG.warn("buildExtNetGroupEntity : Building ext-net group {} entry with drop action since "
234                         + "no egress actions were found for subnet {} extInterface {}",
235                         groupId, subnetName, extInterface);
236             }
237             actionList.add(new ActionDrop());
238         } else {
239             LOG.trace("Building ext-net group {} entry for subnet {} extInterface {} macAddress {}",
240                       groupId, subnetName, extInterface, macAddress);
241             actionList.add(new ActionSetFieldEthernetDestination(setFieldEthDestActionPos, new MacAddress(macAddress)));
242             actionList.addAll(egressActionList);
243         }
244
245         List<BucketInfo> listBucketInfo = new ArrayList<>();
246         listBucketInfo.add(new BucketInfo(actionList));
247         return MDSALUtil.buildGroupEntity(dpnId, groupId, subnetName, GroupTypes.GroupAll, listBucketInfo);
248     }
249
250     @Nullable
251     private GroupEntity buildEmptyExtNetGroupEntity(String subnetName, long groupId, String extInterface) {
252         BigInteger dpId = NatUtil.getDpnForInterface(odlInterfaceRpcService, extInterface);
253         if (BigInteger.ZERO.equals(dpId)) {
254             LOG.error("buildEmptyExtNetGroupEntity: No DPN for interface {}. NAT ext-net flow will not be installed "
255                     + "for subnet {}", extInterface, subnetName);
256             return null;
257         }
258
259         return MDSALUtil.buildGroupEntity(dpId, groupId, subnetName, GroupTypes.GroupAll, new ArrayList<>());
260     }
261 }