Fixup Augmentable and Identifiable methods changing
[netvirt.git] / dhcpservice / impl / src / main / java / org / opendaylight / netvirt / dhcpservice / DhcpNeutronPortListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.dhcpservice;
9
10 import java.math.BigInteger;
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.Locale;
14 import java.util.function.Consumer;
15 import javax.annotation.PostConstruct;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Named;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.genius.datastoreutils.AsyncClusteredDataTreeChangeListenerBase;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
25 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
26 import org.opendaylight.genius.mdsalutil.NwConstants;
27 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
28 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
29 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
30 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput.ArpReponderInputBuilder;
31 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
32 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
33 import org.opendaylight.netvirt.elanmanager.api.IElanService;
34 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
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.itm.rpcs.rev160406.ItmRpcService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 @Singleton
49 public class DhcpNeutronPortListener
50         extends AsyncClusteredDataTreeChangeListenerBase<Port, DhcpNeutronPortListener> {
51
52     private static final Logger LOG = LoggerFactory.getLogger(DhcpNeutronPortListener.class);
53     private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
54     private final IElanService elanService;
55     private final DataBroker broker;
56     private final ManagedNewTransactionRunner txRunner;
57     private final DhcpserviceConfig config;
58     private final IInterfaceManager interfaceManager;
59     private final JobCoordinator jobCoordinator;
60     private final DhcpManager dhcpManager;
61     private final ItmRpcService itmRpcService;
62
63     @Inject
64     public DhcpNeutronPortListener(DataBroker db, DhcpExternalTunnelManager dhcpExternalTunnelManager,
65             @Named("elanService") IElanService ielanService, IInterfaceManager interfaceManager,
66             DhcpserviceConfig config, final JobCoordinator jobCoordinator, DhcpManager dhcpManager,
67             ItmRpcService itmRpcService) {
68
69         super(Port.class, DhcpNeutronPortListener.class);
70         this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
71         this.elanService = ielanService;
72         this.interfaceManager = interfaceManager;
73         this.broker = db;
74         this.txRunner = new ManagedNewTransactionRunnerImpl(db);
75         this.config = config;
76         this.jobCoordinator = jobCoordinator;
77         this.dhcpManager = dhcpManager;
78         this.itmRpcService = itmRpcService;
79     }
80
81     @PostConstruct
82     public void init() {
83         if (config.isControllerDhcpEnabled()) {
84             registerListener(LogicalDatastoreType.CONFIGURATION, broker);
85         }
86     }
87
88     @Override
89     protected InstanceIdentifier<Port> getWildCardPath() {
90         return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
91     }
92
93     @Override
94     @PreDestroy
95     public void close() {
96         super.close();
97         LOG.debug("DhcpNeutronPortListener Listener Closed");
98     }
99
100     @Override
101     protected void remove(InstanceIdentifier<Port> identifier, Port del) {
102         LOG.trace("Port removed: {}", del);
103         if (NeutronConstants.IS_ODL_DHCP_PORT.test(del)) {
104             jobCoordinator.enqueueJob(getJobKey(del),
105                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
106                     java.util.Optional<String> ip4Address = DhcpServiceUtils.getIpV4Address(del);
107                     if (ip4Address.isPresent()) {
108                         dhcpExternalTunnelManager.addOrRemoveDhcpArpFlowforElan(del.getNetworkId().getValue(),
109                                 false, ip4Address.get(), del.getMacAddress().getValue());
110                     }
111                     DhcpServiceUtils.removeSubnetDhcpPortData(del, subnetDhcpPortIdfr -> tx
112                             .delete(LogicalDatastoreType.CONFIGURATION, subnetDhcpPortIdfr));
113                     processArpResponderForElanDpns(del, arpInput -> {
114                         LOG.trace("Removing ARPResponder Flows  for dhcp port {} with ipaddress {} with mac {} "
115                                         + " on dpn {}. ",arpInput.getInterfaceName(), arpInput.getSpa(),
116                                         arpInput.getSha(), arpInput.getDpId());
117                         elanService.removeArpResponderFlow(arpInput);
118                     });
119                 })));
120         }
121         if (isVnicTypeDirectOrMacVtap(del)) {
122             removePort(del);
123         }
124     }
125
126     private String getJobKey(Port port) {
127         return "PORT- " + port.getUuid().getValue();
128     }
129
130     @Override
131     protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
132         LOG.trace("Port changed to {}", update);
133         //With Ipv6 changes we can get ipv4 subnets later. The below check is to support such scenario.
134         if (original.getFixedIps().size() < update.getFixedIps().size()) {
135             final String interfaceName = update.getUuid().getValue();
136             List<FixedIps> updatedFixedIps = update.getFixedIps();
137             // Need to check only the newly added fixed ip.
138             updatedFixedIps.removeAll(original.getFixedIps());
139             Subnet subnet = dhcpManager.getNeutronSubnet(updatedFixedIps);
140             if (null == subnet || !subnet.isEnableDhcp()) {
141                 LOG.trace("Subnet is null/not ipv4 or not enabled {}", subnet);
142                 return;
143             }
144             // Binding the DHCP service for an existing port because of subnet change.
145             jobCoordinator.enqueueJob(DhcpServiceUtils.getJobKey(interfaceName),
146                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
147                     LOG.debug("Binding DHCP service for interface {}", interfaceName);
148                     DhcpServiceUtils.bindDhcpService(interfaceName, NwConstants.DHCP_TABLE, tx);
149                 })), DhcpMConstants.RETRY_COUNT);
150             jobCoordinator.enqueueJob(DhcpServiceUtils.getJobKey(interfaceName), () -> {
151                 BigInteger dpnId = interfaceManager.getDpnForInterface(interfaceName);
152                 if (dpnId == null || dpnId.equals(DhcpMConstants.INVALID_DPID)) {
153                     LOG.trace("Unable to install the DHCP flow since dpn is not available");
154                     return Collections.emptyList();
155                 }
156                 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(
157                     tx -> dhcpManager.installDhcpEntries(dpnId,
158                             DhcpServiceUtils.getAndUpdateVmMacAddress(tx, interfaceName, dhcpManager), tx)));
159             }, DhcpMConstants.RETRY_COUNT);
160         }
161         if (!isVnicTypeDirectOrMacVtap(update)) {
162             LOG.trace("Port updated is normal {}", update.getUuid());
163             if (isVnicTypeDirectOrMacVtap(original)) {
164                 LOG.trace("Original Port was direct/macvtap {} so removing flows and cache entry if any",
165                         update.getUuid());
166                 removePort(original);
167             }
168             return;
169         }
170         if (!isVnicTypeDirectOrMacVtap(original)) {
171             LOG.trace("Original port was normal and updated is direct. Calling addPort()");
172             addPort(update);
173             return;
174         }
175         String macOriginal = getMacAddress(original);
176         String macUpdated = getMacAddress(update);
177         String segmentationIdOriginal = DhcpServiceUtils.getSegmentationId(original.getNetworkId(), broker);
178         String segmentationIdUpdated = DhcpServiceUtils.getSegmentationId(update.getNetworkId(), broker);
179         if (macOriginal != null && !macOriginal.equalsIgnoreCase(macUpdated) && segmentationIdOriginal != null
180                 && !segmentationIdOriginal.equalsIgnoreCase(segmentationIdUpdated)) {
181             LOG.trace("Mac/segment id has changed");
182             dhcpExternalTunnelManager.removeVniMacToPortCache(new BigInteger(segmentationIdOriginal), macOriginal);
183             dhcpExternalTunnelManager.updateVniMacToPortCache(new BigInteger(segmentationIdUpdated),
184                     macUpdated, update);
185         }
186     }
187
188     @Override
189     protected void add(InstanceIdentifier<Port> identifier, Port add) {
190         LOG.trace("Port added {}", add);
191         if (NeutronConstants.IS_ODL_DHCP_PORT.test(add)) {
192             jobCoordinator.enqueueJob(getJobKey(add),
193                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
194                     DhcpServiceUtils.createSubnetDhcpPortData(add, (subnetDhcpPortIdfr, subnetToDhcpport) -> tx
195                             .put(LogicalDatastoreType.CONFIGURATION, subnetDhcpPortIdfr, subnetToDhcpport));
196                     processArpResponderForElanDpns(add, arpInput -> {
197                         LOG.trace(
198                                 "Installing ARP RESPONDER Flows  for dhcp port {} ipaddress {} with mac {} on dpn {}",
199                                 arpInput.getInterfaceName(), arpInput.getSpa(), arpInput.getSha(),
200                                 arpInput.getDpId());
201                         ArpReponderInputBuilder builder = new ArpReponderInputBuilder(arpInput);
202                         builder.setInstructions(ArpResponderUtil.getInterfaceInstructions(interfaceManager,
203                                 arpInput.getInterfaceName(), arpInput.getSpa(), arpInput.getSha(), itmRpcService));
204                         elanService.addArpResponderFlow(builder.buildForInstallFlow());
205                     });
206                 })));
207             java.util.Optional<String> ip4Address = DhcpServiceUtils.getIpV4Address(add);
208             if (ip4Address.isPresent()) {
209                 dhcpExternalTunnelManager.addOrRemoveDhcpArpFlowforElan(add.getNetworkId().getValue(),
210                         true, ip4Address.get(), add.getMacAddress().getValue());
211             }
212         }
213         if (!isVnicTypeDirectOrMacVtap(add)) {
214             return;
215         }
216         addPort(add);
217     }
218
219     private void removePort(Port port) {
220         String macAddress = getMacAddress(port);
221         Uuid networkId = port.getNetworkId();
222         String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
223         if (segmentationId == null) {
224             return;
225         }
226         List<BigInteger> listOfDpns = DhcpServiceUtils.getListOfDpns(broker);
227         dhcpExternalTunnelManager.unInstallDhcpFlowsForVms(networkId.getValue(), listOfDpns, macAddress);
228         dhcpExternalTunnelManager.removeVniMacToPortCache(new BigInteger(segmentationId), macAddress);
229     }
230
231     private void addPort(Port port) {
232         String macAddress = getMacAddress(port);
233         Uuid networkId = port.getNetworkId();
234         String segmentationId = DhcpServiceUtils.getSegmentationId(networkId, broker);
235         if (segmentationId == null) {
236             LOG.trace("segmentation id is null");
237             return;
238         }
239         dhcpExternalTunnelManager.updateVniMacToPortCache(new BigInteger(segmentationId), macAddress, port);
240
241     }
242
243     private String getMacAddress(Port port) {
244         return port.getMacAddress().getValue();
245     }
246
247     private boolean isVnicTypeDirectOrMacVtap(Port port) {
248         PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
249         if (portBinding == null || portBinding.getVnicType() == null) {
250             // By default, VNIC_TYPE is NORMAL
251             return false;
252         }
253         String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
254         return vnicType.equals("direct") || vnicType.equals("macvtap");
255     }
256
257     @Override
258     protected DhcpNeutronPortListener getDataTreeChangeListener() {
259         return DhcpNeutronPortListener.this;
260     }
261
262     /**
263      * Handle(Add/Remove) ARP Responder for DHCP IP on all the DPNs when DHCP is
264      * enabled/disabled on subnet add or update or delete.
265      *
266      * @param port
267      *            DHCP port for which ARP Responder flow to be added when dhcp
268      *            flag is enabled on the subnet or DHCP port for which ARP
269      *            Responder flow to be removed when dhcp flag is disabled on the
270      *            Subnet
271      * @param arpResponderAction
272      *            ARP Responder Action to be performed i.e., add or remove flow
273      */
274     private void processArpResponderForElanDpns(Port port, Consumer<ArpResponderInput> arpResponderAction) {
275
276         java.util.Optional<String> ip4Address = DhcpServiceUtils.getIpV4Address(port);
277         if (!ip4Address.isPresent()) {
278             LOG.warn("There is no IPv4Address for port {}, not performing ARP responder add/remove flow operation",
279                     port.getName());
280             return;
281         }
282         ElanHelper.getDpnInterfacesInElanInstance(broker, port.getNetworkId().getValue()).stream()
283                 .map(ifName -> DhcpServiceUtils.getInterfaceInfo(interfaceManager, ifName)).forEach(interfaceInfo -> {
284                     ArpResponderInput arpResponderInput = new ArpResponderInput.ArpReponderInputBuilder()
285                             .setDpId(interfaceInfo.getDpId()).setInterfaceName(interfaceInfo.getInterfaceName())
286                             .setLportTag(interfaceInfo.getInterfaceTag()).setSha(port.getMacAddress().getValue())
287                             .setSpa(ip4Address.get()).build();
288                     arpResponderAction.accept(arpResponderInput);
289                 });
290
291     }
292
293 }