10650acd8927b80d204a16a545c0d59496f9acab
[netvirt.git] / dhcpservice / impl / src / main / java / org / opendaylight / netvirt / dhcpservice / DhcpServiceUtils.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
9 package org.opendaylight.netvirt.dhcpservice;
10
11 import static org.opendaylight.mdsal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
12
13 import java.math.BigInteger;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.Optional;
23 import java.util.concurrent.CopyOnWriteArrayList;
24 import java.util.concurrent.ExecutionException;
25 import java.util.function.BiConsumer;
26 import java.util.function.Consumer;
27 import java.util.stream.Collectors;
28 import java.util.stream.LongStream;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.genius.datastoreutils.ExpectedDataObjectNotFoundException;
32 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
33 import org.opendaylight.genius.infra.Datastore.Configuration;
34 import org.opendaylight.genius.infra.Datastore.Operational;
35 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
36 import org.opendaylight.genius.infra.TypedWriteTransaction;
37 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
38 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
39 import org.opendaylight.genius.mdsalutil.ActionInfo;
40 import org.opendaylight.genius.mdsalutil.FlowEntity;
41 import org.opendaylight.genius.mdsalutil.InstructionInfo;
42 import org.opendaylight.genius.mdsalutil.MDSALUtil;
43 import org.opendaylight.genius.mdsalutil.MatchInfo;
44 import org.opendaylight.genius.mdsalutil.NWUtil;
45 import org.opendaylight.genius.mdsalutil.NwConstants;
46 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
47 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
48 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
49 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
50 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
51 import org.opendaylight.genius.mdsalutil.matches.MatchArpTpa;
52 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetSource;
53 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
54 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
55 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
56 import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
57 import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
58 import org.opendaylight.genius.utils.ServiceIndex;
59 import org.opendaylight.mdsal.binding.api.DataBroker;
60 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
61 import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
62 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
63 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkMaps;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMapKey;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.IpVersionBase;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.IpVersionV4;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.NetworkTypeVxlan;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.Networks;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.NetworkKey;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.InterfaceNameMacAddresses;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.SubnetDhcpPortData;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddress;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddressBuilder;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710._interface.name.mac.addresses.InterfaceNameMacAddressKey;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPort;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPortBuilder;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.api.rev150710.subnet.dhcp.port.data.SubnetToDhcpPortKey;
111 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
112 import org.opendaylight.yangtools.yang.common.Uint64;
113 import org.slf4j.Logger;
114 import org.slf4j.LoggerFactory;
115
116 public final class DhcpServiceUtils {
117     private static final Logger LOG = LoggerFactory.getLogger(DhcpServiceUtils.class);
118
119     private static List<Uint64> connectedDpnIds = new CopyOnWriteArrayList<>();
120
121     private DhcpServiceUtils() {
122
123     }
124
125     public static void setupDhcpFlowEntry(@Nullable Uint64 dpId, short tableId, @Nullable String vmMacAddress,
126                                           int addOrRemove,
127                                           IMdsalApiManager mdsalUtil, DhcpServiceCounters dhcpServiceCounters,
128                                           TypedReadWriteTransaction<Configuration> tx)
129             throws ExecutionException, InterruptedException {
130         if (dpId == null || dpId.equals(DhcpMConstants.INVALID_DPID) || vmMacAddress == null) {
131             return;
132         }
133         List<MatchInfo> matches = getDhcpMatch(vmMacAddress);
134         List<InstructionInfo> instructions = new ArrayList<>();
135         List<ActionInfo> actionsInfos = new ArrayList<>();
136
137         // Punt to controller
138         actionsInfos.add(new ActionPuntToController());
139         instructions.add(new InstructionApplyActions(actionsInfos));
140         if (addOrRemove == NwConstants.DEL_FLOW) {
141             LOG.trace("Removing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
142             dhcpServiceCounters.removeDhcpFlow();
143             mdsalUtil.removeFlow(tx, dpId, getDhcpFlowRef(dpId, tableId, vmMacAddress), tableId);
144         } else {
145             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
146                     getDhcpFlowRef(dpId, tableId, vmMacAddress), DhcpMConstants.DEFAULT_DHCP_FLOW_PRIORITY,
147                     "DHCP", 0, 0, DhcpMConstants.COOKIE_DHCP_BASE, matches, instructions);
148             LOG.trace("Installing DHCP Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
149             dhcpServiceCounters.installDhcpFlow();
150             mdsalUtil.addFlow(tx, flowEntity);
151         }
152     }
153
154     private static String getDhcpFlowRef(Uint64 dpId, long tableId, String vmMacAddress) {
155         return new StringBuilder().append(DhcpMConstants.FLOWID_PREFIX)
156                 .append(dpId.toString()).append(NwConstants.FLOWID_SEPARATOR)
157                 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
158                 .append(vmMacAddress).toString();
159     }
160
161     private static String getDhcpArpFlowRef(Uint64 dpId, long tableId, long lportTag, String ipAddress) {
162         return new StringBuilder().append(DhcpMConstants.FLOWID_PREFIX)
163                 .append(dpId.toString()).append(NwConstants.FLOWID_SEPARATOR)
164                 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
165                 .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
166                 .append(ipAddress).toString();
167     }
168
169     public static void setupDhcpDropAction(Uint64 dpId, short tableId, String vmMacAddress, int addOrRemove,
170                                            IMdsalApiManager mdsalUtil, DhcpServiceCounters dhcpServiceCounters,
171                                            TypedReadWriteTransaction<Configuration> tx)
172             throws ExecutionException, InterruptedException {
173         if (dpId == null || dpId.equals(DhcpMConstants.INVALID_DPID) || vmMacAddress == null) {
174             return;
175         }
176         List<MatchInfo> matches = getDhcpMatch(vmMacAddress);
177
178         List<ActionInfo> actionsInfos = new ArrayList<>();
179         List<InstructionInfo> instructions = new ArrayList<>();
180         instructions.add(new InstructionApplyActions(actionsInfos));
181         // Drop Action
182         actionsInfos.add(new ActionDrop());
183         if (addOrRemove == NwConstants.DEL_FLOW) {
184             LOG.trace("Removing DHCP Drop Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
185             dhcpServiceCounters.removeDhcpDropFlow();
186             mdsalUtil.removeFlow(tx, dpId, getDhcpFlowRef(dpId, tableId, vmMacAddress), tableId);
187         } else {
188             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
189                     getDhcpFlowRef(dpId, tableId, vmMacAddress), DhcpMConstants.DEFAULT_DHCP_FLOW_PRIORITY,
190                     "DHCP", 0, 0, DhcpMConstants.COOKIE_DHCP_BASE, matches, instructions);
191             LOG.trace("Installing DHCP Drop Flow DpId {}, vmMacAddress {}", dpId, vmMacAddress);
192             dhcpServiceCounters.installDhcpDropFlow();
193             mdsalUtil.addFlow(tx, flowEntity);
194         }
195     }
196
197     public static void setupDhcpArpRequest(Uint64 dpId, short tableId, Uint64 vni, String dhcpIpAddress,
198                                            int lportTag, @Nullable Long elanTag, boolean add,
199                                            IMdsalApiManager mdsalUtil, TypedReadWriteTransaction<Configuration> tx)
200             throws ExecutionException, InterruptedException {
201         List<MatchInfo> matches = getDhcpArpMatch(vni, dhcpIpAddress);
202         if (add) {
203             Flow flow = MDSALUtil.buildFlowNew(tableId, getDhcpArpFlowRef(dpId, tableId, lportTag, dhcpIpAddress),
204                     DhcpMConstants.DEFAULT_DHCP_ARP_FLOW_PRIORITY, "DHCPArp", 0, 0,
205                     generateDhcpArpCookie(lportTag, dhcpIpAddress), matches, null);
206             LOG.trace("Removing DHCP ARP Flow DpId {}, DHCP Port IpAddress {}", dpId, dhcpIpAddress);
207             mdsalUtil.removeFlow(tx, dpId, flow);
208         } else {
209             Flow flow = MDSALUtil.buildFlowNew(tableId, getDhcpArpFlowRef(dpId, tableId, lportTag, dhcpIpAddress),
210                     DhcpMConstants.DEFAULT_DHCP_ARP_FLOW_PRIORITY, "DHCPArp", 0, 0,
211                     generateDhcpArpCookie(lportTag, dhcpIpAddress), matches,
212                     getDhcpArpInstructions(elanTag, lportTag));
213             LOG.trace("Adding DHCP ARP Flow DpId {}, DHCPPort IpAddress {}", dpId, dhcpIpAddress);
214             mdsalUtil.addFlow(tx, dpId, flow);
215         }
216     }
217
218     public static List<MatchInfo> getDhcpMatch() {
219         List<MatchInfo> matches = new ArrayList<>();
220         matches.add(MatchEthernetType.IPV4);
221         matches.add(MatchIpProtocol.UDP);
222         matches.add(new MatchUdpSourcePort(DhcpMConstants.DHCP_CLIENT_PORT));
223         matches.add(new MatchUdpDestinationPort(DhcpMConstants.DHCP_SERVER_PORT));
224         return matches;
225     }
226
227     public static List<MatchInfo> getDhcpMatch(String vmMacAddress) {
228         List<MatchInfo> matches = getDhcpMatch();
229         matches.add(new MatchEthernetSource(new MacAddress(vmMacAddress)));
230         return matches;
231     }
232
233     private static List<MatchInfo> getDhcpArpMatch(Uint64 vni, String ipAddress) {
234         return Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST, new MatchTunnelId(vni),
235                 new MatchArpTpa(ipAddress, "32"));
236     }
237
238     private static List<Instruction> getDhcpArpInstructions(Long elanTag, int lportTag) {
239         List<Instruction> mkInstructions = new ArrayList<>();
240         int instructionKey = 0;
241         mkInstructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(
242                 ElanHelper.getElanMetadataLabel(elanTag, lportTag), ElanHelper.getElanMetadataMask(),
243                 ++instructionKey));
244         mkInstructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ARP_RESPONDER_TABLE,
245                 ++instructionKey));
246         return mkInstructions;
247     }
248
249     private static Uint64 generateDhcpArpCookie(int lportTag, String ipAddress) {
250         try {
251             return Uint64.valueOf(NwConstants.TUNNEL_TABLE_COOKIE.toJava().add(BigInteger.valueOf(255))
252                     .add(BigInteger.valueOf(NWUtil.convertInetAddressToLong(InetAddress.getByName(ipAddress))))
253                     .add(BigInteger.valueOf(lportTag)));
254         } catch (UnknownHostException e) {
255             return Uint64.valueOf(NwConstants.TUNNEL_TABLE_COOKIE.toJava().add(BigInteger.valueOf(lportTag)));
256         }
257     }
258
259     public static List<Uint64> getListOfDpns(DataBroker broker) {
260         if (!connectedDpnIds.isEmpty()) {
261             return connectedDpnIds;
262         }
263         try {
264             return extractDpnsFromNodes(MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
265                     InstanceIdentifier.builder(Nodes.class).build()));
266         } catch (ExecutionException | InterruptedException e) {
267             LOG.error("getListOfDpns: Exception while reading getListOfDpns DS", e);
268             return Collections.emptyList();
269         }
270     }
271
272     @NonNull
273     private static List<Uint64> extractDpnsFromNodes(Optional<Nodes> optionalNodes) {
274         return optionalNodes.map(
275             nodes -> nodes.nonnullNode().stream().map(Node::getId).filter(Objects::nonNull).map(
276                     MDSALUtil::getDpnIdFromNodeName).collect(
277                     Collectors.toList())).orElse(Collections.emptyList());
278     }
279
280     @NonNull
281     public static List<Uint64> getDpnsForElan(String elanInstanceName, DataBroker broker) {
282         List<Uint64> elanDpns = new LinkedList<>();
283         InstanceIdentifier<ElanDpnInterfacesList> elanDpnInstanceIdentifier =
284                 InstanceIdentifier.builder(ElanDpnInterfaces.class)
285                         .child(ElanDpnInterfacesList.class, new ElanDpnInterfacesListKey(elanInstanceName)).build();
286         Optional<ElanDpnInterfacesList> elanDpnOptional;
287         try {
288             elanDpnOptional = SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.OPERATIONAL,
289                     elanDpnInstanceIdentifier);
290         } catch (ExecutionException | InterruptedException e) {
291             LOG.error("getDpnsForElan: Exception while reading ElanDpnInterfacesList DS for the elanInstanceName {}",
292                     elanInstanceName, e);
293             return Collections.emptyList();
294         }
295         if (elanDpnOptional.isPresent()) {
296             List<DpnInterfaces> dpns = elanDpnOptional.get().nonnullDpnInterfaces();
297             for (DpnInterfaces dpnInterfaces : dpns) {
298                 elanDpns.add(dpnInterfaces.getDpId());
299             }
300         }
301         return elanDpns;
302     }
303
304     public static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
305             .@Nullable Interface getInterfaceFromOperationalDS(String interfaceName, DataBroker dataBroker) {
306         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
307                 .state.InterfaceKey interfaceKey =
308                 new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
309                         .state.InterfaceKey(interfaceName);
310         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
311                 .interfaces.state.Interface> interfaceId = InstanceIdentifier.builder(InterfacesState.class)
312                 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
313                         .interfaces.state.Interface.class, interfaceKey).build();
314         return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, interfaceId, dataBroker).orElse(null);
315     }
316
317
318     @Nullable
319     public static String getSegmentationId(Uuid networkId, DataBroker broker) {
320         InstanceIdentifier<Network> inst = InstanceIdentifier.create(Neutron.class)
321                 .child(Networks.class).child(Network.class, new NetworkKey(networkId));
322         Optional<Network> optionalNetwork;
323         try {
324             optionalNetwork = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, inst);
325         } catch (ExecutionException | InterruptedException e) {
326             LOG.error("getSegmentationId: Exception while reading Network DS for the Network {}",
327                     networkId.getValue(), e);
328             return null;
329         }
330         if (!optionalNetwork.isPresent()) {
331             return null;
332         }
333         Network network = optionalNetwork.get();
334         String segmentationId = NeutronUtils.getSegmentationIdFromNeutronNetwork(network, NetworkTypeVxlan.class);
335         return segmentationId;
336     }
337
338     public static String getJobKey(String interfaceName) {
339         return new StringBuilder().append(DhcpMConstants.DHCP_JOB_KEY_PREFIX).append(interfaceName).toString();
340     }
341
342     public static void bindDhcpService(String interfaceName, short tableId, TypedWriteTransaction<Configuration> tx) {
343         int instructionKey = 0;
344         List<Instruction> instructions = new ArrayList<>();
345         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
346         short serviceIndex = ServiceIndex.getIndex(NwConstants.DHCP_SERVICE_NAME, NwConstants.DHCP_SERVICE_INDEX);
347         BoundServices
348                 serviceInfo =
349                 getBoundServices(String.format("%s.%s", "dhcp", interfaceName),
350                         serviceIndex, DhcpMConstants.DEFAULT_FLOW_PRIORITY,
351                         DhcpMConstants.COOKIE_VM_INGRESS_TABLE, instructions);
352         tx.put(buildServiceId(interfaceName, serviceIndex), serviceInfo, CREATE_MISSING_PARENTS);
353     }
354
355     public static void unbindDhcpService(String interfaceName, TypedWriteTransaction<Configuration> tx) {
356         short serviceIndex = ServiceIndex.getIndex(NwConstants.DHCP_SERVICE_NAME, NwConstants.DHCP_SERVICE_INDEX);
357         tx.delete(buildServiceId(interfaceName, serviceIndex));
358     }
359
360     private static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName,
361                                                              short dhcpServicePriority) {
362         return InstanceIdentifier.builder(ServiceBindings.class)
363                 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
364                 .child(BoundServices.class, new BoundServicesKey(dhcpServicePriority)).build();
365     }
366
367     public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
368                                           Uint64 cookie, List<Instruction> instructions) {
369         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
370                 .setFlowPriority(flowPriority).setInstruction(instructions);
371         return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority))
372                 .setServiceName(serviceName).setServicePriority(servicePriority)
373                 .setServiceType(ServiceTypeFlowBased.class)
374                 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
375     }
376
377     @SuppressWarnings("checkstyle:IllegalCatch")
378     protected static void createSubnetDhcpPortData(Port port,
379             BiConsumer<InstanceIdentifier<SubnetToDhcpPort>, SubnetToDhcpPort> consumer) {
380         java.util.Optional<String> ip4Address = getIpV4Address(port);
381         java.util.Optional<String> subnetId = getNeutronSubnetId(port);
382         if (!(ip4Address.isPresent() && subnetId.isPresent())) {
383             return;
384         }
385         LOG.trace("Adding SubnetPortData entry for subnet {}", subnetId.get());
386         InstanceIdentifier<SubnetToDhcpPort> identifier = buildSubnetToDhcpPort(subnetId.get());
387         SubnetToDhcpPort subnetToDhcpPort = getSubnetToDhcpPort(port, subnetId.get(), ip4Address.get());
388         try {
389             LOG.trace("Adding to SubnetToDhcpPort subnet {}  mac {}.", subnetId.get(),
390                     port.getMacAddress().getValue());
391             consumer.accept(identifier, subnetToDhcpPort);
392         } catch (Exception e) {
393             LOG.error("Failure while creating SubnetToDhcpPort map for network {}.", port.getNetworkId(), e);
394         }
395     }
396
397     @SuppressWarnings("checkstyle:IllegalCatch")
398     protected static void removeSubnetDhcpPortData(Port port, Consumer<InstanceIdentifier<SubnetToDhcpPort>> consumer) {
399         String subnetId = port.getDeviceId().substring("OpenDaylight".length() + 1);
400         LOG.trace("Removing NetworkPortData entry for Subnet {}", subnetId);
401         InstanceIdentifier<SubnetToDhcpPort> identifier = buildSubnetToDhcpPort(subnetId);
402         try {
403             consumer.accept(identifier);
404             LOG.trace("Deleted SubnetDhcpPort for Subnet {}", subnetId);
405         } catch (Exception e) {
406             LOG.error("Failure while removing SubnetToDhcpPort for subnet {}.", subnetId, e);
407         }
408
409     }
410
411     static InstanceIdentifier<SubnetToDhcpPort> buildSubnetToDhcpPort(String subnetId) {
412         return InstanceIdentifier.builder(SubnetDhcpPortData.class)
413                 .child(SubnetToDhcpPort.class, new SubnetToDhcpPortKey(subnetId)).build();
414     }
415
416     public static java.util.Optional<SubnetToDhcpPort> getSubnetDhcpPortData(DataBroker broker, String subnetId) {
417         InstanceIdentifier<SubnetToDhcpPort> id = buildSubnetToDhcpPort(subnetId);
418         try {
419             return java.util.Optional
420                     .ofNullable(SingleTransactionDataBroker.syncRead(broker, LogicalDatastoreType.CONFIGURATION, id));
421         } catch (ExpectedDataObjectNotFoundException e) {
422             LOG.warn("Failed to read SubnetToDhcpPort for DS due to error {}", e.getMessage());
423         }
424         return java.util.Optional.empty();
425     }
426
427     static IpAddress convertLongToIp(long ip) {
428         String[] array = LongStream.of(24, 16, 8, 0) //
429                 .map(x -> ip >> x & 0xFF).boxed() //
430                 .map(String::valueOf) //
431                 .toArray(String[]::new);
432         return IpAddressBuilder.getDefaultInstance(String.join(".", array));
433     }
434
435     static long convertIpToLong(IpAddress ipa) {
436         String[] splitIp = ipa.stringValue().split("\\.");
437         long result = 0;
438         for (String part : splitIp) {
439             result <<= 8;
440             result |= Integer.parseInt(part);
441         }
442         return result;
443     }
444
445
446     static SubnetToDhcpPort getSubnetToDhcpPort(Port port, String subnetId, String ipAddress) {
447         return new SubnetToDhcpPortBuilder()
448                 .withKey(new SubnetToDhcpPortKey(subnetId))
449                 .setSubnetId(subnetId).setPortName(port.getUuid().getValue())
450                 .setPortMacaddress(port.getMacAddress().getValue()).setPortFixedip(ipAddress).build();
451     }
452
453     static InterfaceInfo getInterfaceInfo(IInterfaceManager interfaceManager, String interfaceName) {
454         return interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
455     }
456
457     public static java.util.Optional<String> getIpV4Address(Port port) {
458         if (port.getFixedIps() == null) {
459             return java.util.Optional.empty();
460         }
461         return port.getFixedIps().stream().filter(DhcpServiceUtils::isIpV4AddressAvailable)
462                 .map(v -> v.getIpAddress().getIpv4Address().getValue()).findFirst();
463     }
464
465     public static java.util.Optional<String> getNeutronSubnetId(Port port) {
466         if (port.getFixedIps() == null) {
467             return java.util.Optional.empty();
468         }
469         return port.getFixedIps().stream().filter(DhcpServiceUtils::isIpV4AddressAvailable)
470                 .map(v -> v.getSubnetId().getValue()).findFirst();
471     }
472
473     public static boolean isIpV4AddressAvailable(FixedIps fixedIp) {
474         return fixedIp != null && fixedIp.getIpAddress() != null && fixedIp.getIpAddress().getIpv4Address() != null;
475     }
476
477     @Nullable
478     public static String getAndUpdateVmMacAddress(TypedReadWriteTransaction<Operational> tx, String interfaceName,
479             DhcpManager dhcpManager) throws ExecutionException, InterruptedException {
480         InstanceIdentifier<InterfaceNameMacAddress> instanceIdentifier =
481                 InstanceIdentifier.builder(InterfaceNameMacAddresses.class)
482                         .child(InterfaceNameMacAddress.class, new InterfaceNameMacAddressKey(interfaceName)).build();
483         Optional<InterfaceNameMacAddress> existingEntry = tx.read(instanceIdentifier).get();
484         if (!existingEntry.isPresent()) {
485             LOG.trace("Entry for interface {} missing in InterfaceNameVmMacAddress map", interfaceName);
486             String vmMacAddress = getNeutronMacAddress(interfaceName, dhcpManager);
487             if (vmMacAddress == null || vmMacAddress.isEmpty()) {
488                 return null;
489             }
490             LOG.trace("Updating InterfaceNameVmMacAddress map with {}, {}", interfaceName,vmMacAddress);
491             InterfaceNameMacAddress interfaceNameMacAddress =
492                     new InterfaceNameMacAddressBuilder()
493                             .withKey(new InterfaceNameMacAddressKey(interfaceName))
494                             .setInterfaceName(interfaceName).setMacAddress(vmMacAddress).build();
495             tx.merge(instanceIdentifier, interfaceNameMacAddress, CREATE_MISSING_PARENTS);
496             return vmMacAddress;
497         }
498         return existingEntry.get().getMacAddress();
499     }
500
501     @Nullable
502     private static String getNeutronMacAddress(String interfaceName, DhcpManager dhcpManager) {
503         Port port = dhcpManager.getNeutronPort(interfaceName);
504         if (port != null) {
505             LOG.trace("Port found in neutron. Interface Name {}, port {}", interfaceName, port);
506             return port.getMacAddress().getValue();
507         }
508         return null;
509     }
510
511     @NonNull
512     public static List<Uuid> getSubnetIdsFromNetworkId(DataBroker broker, Uuid networkId) {
513         InstanceIdentifier id = buildNetworkMapIdentifier(networkId);
514         Optional<NetworkMap> optionalNetworkMap;
515         try {
516             optionalNetworkMap = SingleTransactionDataBroker.syncReadOptional(broker,
517                     LogicalDatastoreType.CONFIGURATION, id);
518         } catch (ExecutionException | InterruptedException e) {
519             LOG.error("getSubnetIdsFromNetworkId: Exception while reading NetworkMap DS for the network {}",
520                     networkId.getValue(), e);
521             return Collections.emptyList();
522         }
523         if (optionalNetworkMap.isPresent()) {
524             @Nullable List<Uuid> subnetIdList = optionalNetworkMap.get().getSubnetIdList();
525             if (subnetIdList != null) {
526                 return subnetIdList;
527             }
528         }
529         return Collections.emptyList();
530     }
531
532     static InstanceIdentifier<NetworkMap> buildNetworkMapIdentifier(Uuid networkId) {
533         return InstanceIdentifier.builder(NetworkMaps.class).child(NetworkMap.class,
534             new NetworkMapKey(networkId)).build();
535     }
536
537     public static boolean isIpv4Subnet(DataBroker broker, Uuid subnetUuid) {
538         final SubnetKey subnetkey = new SubnetKey(subnetUuid);
539         final InstanceIdentifier<Subnet> subnetidentifier = InstanceIdentifier.create(Neutron.class)
540                 .child(Subnets.class).child(Subnet.class, subnetkey);
541         final Optional<Subnet> subnet;
542         try {
543             subnet = SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION,
544                     subnetidentifier);
545         } catch (ExecutionException | InterruptedException e) {
546             LOG.error("isIpv4Subnet: Exception while reading Subnet DS for the Subnet {}",
547                     subnetUuid.getValue(), e);
548             return false;
549         }
550         if (subnet.isPresent()) {
551             Class<? extends IpVersionBase> ipVersionBase = subnet.get().getIpVersion();
552             return IpVersionV4.class.equals(ipVersionBase);
553         }
554         return false;
555     }
556
557     public static void addToDpnIdCache(Uint64 dpnId) {
558         if (!connectedDpnIds.contains(dpnId)) {
559             connectedDpnIds.add(dpnId);
560         }
561     }
562
563     public static void removeFromDpnIdCache(Uint64 dpnId) {
564         connectedDpnIds.remove(dpnId);
565     }
566
567     public static Uint64 getDpnIdFromNodeConnectorId(NodeConnectorId nodeConnectorId) {
568         Long dpIdLong = MDSALUtil.getDpnIdFromPortName(nodeConnectorId);
569         return dpIdLong < 0 ? Uint64.ZERO : Uint64.valueOf(dpIdLong);
570     }
571 }
572