Avoid full elan-interface read for gre/vxlan ext-network
[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 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.List;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
22 import org.opendaylight.genius.mdsalutil.ActionInfo;
23 import org.opendaylight.genius.mdsalutil.BucketInfo;
24 import org.opendaylight.genius.mdsalutil.GroupEntity;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
27 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
28 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.netvirt.elanmanager.api.IElanService;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkAttributes;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
39 import org.opendaylight.yangtools.yang.common.Uint32;
40 import org.opendaylight.yangtools.yang.common.Uint64;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 @Singleton
45 public class ExternalNetworkGroupInstaller {
46     private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkGroupInstaller.class);
47     private final DataBroker broker;
48     private final IMdsalApiManager mdsalManager;
49     private final IElanService elanService;
50     private final IdManagerService idManager;
51     private final OdlInterfaceRpcService odlInterfaceRpcService;
52     private final JobCoordinator coordinator;
53     private final ItmRpcService itmRpcService;
54     private final IInterfaceManager interfaceManager;
55     private final NatServiceCounters natServiceCounters;
56
57     @Inject
58     public ExternalNetworkGroupInstaller(final DataBroker broker, final IMdsalApiManager mdsalManager,
59                                          final IElanService elanService, final IdManagerService idManager,
60                                          final OdlInterfaceRpcService odlInterfaceRpcService,
61                                          final JobCoordinator coordinator, final ItmRpcService itmRpcService,
62                                          final IInterfaceManager interfaceManager,
63                                          NatServiceCounters natServiceCounters) {
64
65         this.broker = broker;
66         this.mdsalManager = mdsalManager;
67         this.elanService = elanService;
68         this.idManager = idManager;
69         this.odlInterfaceRpcService = odlInterfaceRpcService;
70         this.coordinator = coordinator;
71         this.itmRpcService = itmRpcService;
72         this.interfaceManager = interfaceManager;
73         this.natServiceCounters = natServiceCounters;
74     }
75
76     public void installExtNetGroupEntries(Subnetmap subnetMap) {
77         if (subnetMap == null) {
78             LOG.error("installExtNetGroupEntries : Subnetmap is null");
79             return;
80         }
81
82         if (NatUtil.isIPv6Subnet(subnetMap.getSubnetIp())) {
83             LOG.debug("installExtNetGroupEntries : Subnet id {} is not an IPv4 subnet, hence skipping.",
84                     subnetMap.getId());
85             return;
86         }
87
88         Uuid networkId = subnetMap.getNetworkId();
89         Uuid subnetId = subnetMap.getId();
90         NetworkAttributes.NetworkType extNetworkType = subnetMap.getNetworkType();
91         if (externalGroupInstallationRequiredForExtNetwork(networkId, subnetMap.isExternal(),
92             extNetworkType, subnetId)) {
93             String macAddress = NatUtil.getSubnetGwMac(broker, subnetId, networkId.getValue());
94             installExtNetGroupEntries(subnetMap, macAddress);
95         }
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         if (externalGroupInstallationRequiredForExtNetwork(subnetMap.getNetworkId(), subnetMap.isExternal(),
111             subnetMap.getNetworkType(), subnetId)) {
112             installExtNetGroupEntries(subnetMap, macAddress);
113         }
114     }
115
116     public void installExtNetGroupEntries(Uuid networkId, Uint64 dpnId) {
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         Uint32 groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME,
147             NatUtil.getGroupIdKey(subnetName));
148         if (groupId != NatConstants.INVALID_ID) {
149             LOG.info("installExtNetGroupEntries : Installing ext-net group {} entry for subnet {} with macAddress {} "
150                     + "(extInterfaces: {})", groupId, subnetName, macAddress,
151                     Arrays.toString(extInterfaces.toArray()));
152             for (String extInterface : extInterfaces) {
153                 Uint64 dpId = NatUtil.getDpnForInterface(odlInterfaceRpcService, extInterface);
154                 if (Uint64.ZERO.equals(dpId)) {
155                     LOG.info(
156                         "installExtNetGroupEntries: No DPN for interface {}. NAT ext-net flow will not be installed "
157                             + "for subnet {}", extInterface, subnetName);
158                     return;
159                 }
160                 installExtNetGroupEntry(groupId, subnetName, extInterface, macAddress, dpId);
161             }
162         } else {
163             LOG.error("installExtNetGroupEntries: Unable to get groupId for subnet:{}", subnetName);
164         }
165     }
166
167     public void installExtNetGroupEntry(Uuid networkId, Uuid subnetId, Uint64 dpnId, String macAddress) {
168         Subnetmap subnetMap = NatUtil.getSubnetMap(broker, subnetId);
169         if (subnetMap == null) {
170             LOG.error("installExtNetGroupEntries : Subnetmap is null");
171             return;
172         }
173         if (externalGroupInstallationRequiredForExtNetwork(networkId, subnetMap.isExternal(),
174             subnetMap.getNetworkType(), subnetId)) {
175             String subnetName = subnetId.getValue();
176             String extInterface = elanService.getExternalElanInterface(networkId.getValue(), dpnId);
177             if (extInterface == null) {
178                 LOG.warn(
179                     "installExtNetGroupEntry : No external ELAN interface attached to network {} subnet {} DPN id {}",
180                     networkId, subnetName, dpnId);
181                 //return;
182             }
183             Uint32 groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME,
184                 NatUtil.getGroupIdKey(subnetName));
185             if (groupId != NatConstants.INVALID_ID) {
186                 LOG.info(
187                     "installExtNetGroupEntry : Installing ext-net group {} entry for subnet {} with macAddress {} "
188                         + "(extInterface: {})", groupId, subnetName, macAddress, extInterface);
189                 installExtNetGroupEntry(groupId, subnetName, extInterface, macAddress, dpnId);
190             } else {
191                 LOG.error("installExtNetGroupEntry: Unable to get groupId for subnet:{}",
192                     subnetName);
193             }
194         }
195     }
196
197     private void installExtNetGroupEntry(Uint32 groupId, String subnetName, String extInterface,
198             String macAddress, Uint64 dpnId) {
199
200         coordinator.enqueueJob(NatUtil.getDefaultFibRouteToSNATForSubnetJobKey(subnetName, dpnId), () -> {
201             GroupEntity groupEntity = buildExtNetGroupEntity(macAddress, subnetName, groupId, extInterface, dpnId);
202             mdsalManager.syncInstallGroup(groupEntity);
203             return Collections.emptyList();
204         });
205     }
206
207     public void removeExtNetGroupEntries(Subnetmap subnetMap) {
208         if (subnetMap == null) {
209             return;
210         }
211
212         String subnetName = subnetMap.getId().getValue();
213         Uuid networkId = subnetMap.getNetworkId();
214         NetworkAttributes.NetworkType extNetworkType = subnetMap.getNetworkType();
215         if (externalGroupInstallationRequiredForExtNetwork(networkId, subnetMap.isExternal(),
216             extNetworkType, subnetMap.getId())) {
217             Collection<String> extInterfaces = elanService.getExternalElanInterfaces(networkId.getValue());
218             if (extInterfaces == null || extInterfaces.isEmpty()) {
219                 LOG.debug(
220                     "removeExtNetGroupEntries : No external ELAN interfaces attached to network {} subnet {}",
221                     networkId, subnetName);
222                 return;
223             }
224
225             Uint32 groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME,
226                 NatUtil.getGroupIdKey(subnetName));
227             if (groupId != NatConstants.INVALID_ID) {
228                 for (String extInterface : extInterfaces) {
229                     GroupEntity groupEntity = buildEmptyExtNetGroupEntity(subnetName, groupId,
230                         extInterface);
231                     if (groupEntity != null) {
232                         LOG.info(
233                             "removeExtNetGroupEntries : Remove ext-net Group: id {}, subnet id {}",
234                             groupId, subnetName);
235                         natServiceCounters.removeExternalNetworkGroup();
236                         mdsalManager.syncRemoveGroup(groupEntity);
237                     }
238                 }
239             } else {
240                 LOG.error("removeExtNetGroupEntries: Unable to get groupId for subnet:{}",
241                     subnetName);
242             }
243         }
244     }
245
246     private GroupEntity buildExtNetGroupEntity(String macAddress, String subnetName,
247                                                Uint32 groupId, String extInterface, Uint64 dpnId) {
248
249         List<ActionInfo> actionList = new ArrayList<>();
250         final int setFieldEthDestActionPos = 0;
251         List<ActionInfo> egressActionList = new ArrayList<>();
252         if (extInterface != null) {
253             egressActionList = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmRpcService,
254                     interfaceManager, extInterface, null, setFieldEthDestActionPos + 1, false);
255         }
256         if (Strings.isNullOrEmpty(macAddress) || egressActionList.isEmpty()) {
257             if (Strings.isNullOrEmpty(macAddress)) {
258                 LOG.trace("buildExtNetGroupEntity : Building ext-net group {} entry with drop action since "
259                         + "GW mac has not been resolved for subnet {} extInterface {}",
260                         groupId, subnetName, extInterface);
261             } else {
262                 LOG.warn("buildExtNetGroupEntity : Building ext-net group {} entry with drop action since "
263                         + "no egress actions were found for subnet {} extInterface {}",
264                         groupId, subnetName, extInterface);
265             }
266             actionList.add(new ActionDrop());
267         } else {
268             LOG.trace("Building ext-net group {} entry for subnet {} extInterface {} macAddress {}",
269                       groupId, subnetName, extInterface, macAddress);
270             actionList.add(new ActionSetFieldEthernetDestination(setFieldEthDestActionPos, new MacAddress(macAddress)));
271             actionList.addAll(egressActionList);
272         }
273
274         List<BucketInfo> listBucketInfo = new ArrayList<>();
275         listBucketInfo.add(new BucketInfo(actionList));
276         return MDSALUtil.buildGroupEntity(dpnId, groupId.longValue(), subnetName, GroupTypes.GroupAll, listBucketInfo);
277     }
278
279     @Nullable
280     private GroupEntity buildEmptyExtNetGroupEntity(String subnetName, Uint32 groupId, String extInterface) {
281         Uint64 dpId = NatUtil.getDpnForInterface(odlInterfaceRpcService, extInterface);
282         if (Uint64.ZERO.equals(dpId)) {
283             LOG.error("buildEmptyExtNetGroupEntity: No DPN for interface {}. NAT ext-net flow will not be installed "
284                     + "for subnet {}", extInterface, subnetName);
285             return null;
286         }
287
288         return MDSALUtil.buildGroupEntity(dpId, groupId.longValue(), subnetName,
289                 GroupTypes.GroupAll, new ArrayList<>());
290     }
291
292     private boolean externalGroupInstallationRequiredForExtNetwork(Uuid networkId, boolean isExternal,
293         NetworkAttributes.NetworkType extNetworkType,
294         Uuid subnetId) {
295         if (networkId == null || !isExternal) {
296             LOG.debug("externalGroupInstallationRequiredForExtNetwork : network is null or not External Network:{} for"
297                 + " subnet id {}", isExternal, subnetId.getValue());
298             return false;
299         }
300         // Installation of External Network Group is only required for flat/vlan use-cases.
301         if (extNetworkType == null
302             || extNetworkType == NetworkAttributes.NetworkType.GRE
303             || extNetworkType == NetworkAttributes.NetworkType.VXLAN) {
304             LOG.debug("Provider Type is either null or of type {} for network {}, subnet {} for which "
305                     + "ExternalNetwork Group installation not required",
306                 extNetworkType, networkId.getValue(), subnetId.getValue());
307             return false;
308         }
309         return true;
310     }
311 }