Convert itm-impl to use mdsal-binding-util
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / confighelpers / OvsdbTepAddConfigHelper.java
1 /*
2  * Copyright (c) 2016, 2017 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.genius.itm.confighelpers;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Objects;
17 import org.opendaylight.genius.itm.globals.ITMConstants;
18 import org.opendaylight.genius.itm.impl.ItmUtils;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.mdsal.binding.api.DataBroker;
21 import org.opendaylight.mdsal.binding.util.Datastore;
22 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
23 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.NotHostedTransportZones;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.TransportZones;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.TepsInNotHostedTransportZone;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.TepsInNotHostedTransportZoneBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.TepsInNotHostedTransportZoneKey;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.tepsinnothostedtransportzone.UnknownVteps;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.tepsinnothostedtransportzone.UnknownVtepsBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.not.hosted.transport.zones.tepsinnothostedtransportzone.UnknownVtepsKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZone;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.TransportZoneKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.Vteps;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.VtepsBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.transport.zones.transport.zone.VtepsKey;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.common.Uint64;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public final class OvsdbTepAddConfigHelper {
46
47     private static final Logger LOG = LoggerFactory.getLogger(OvsdbTepAddConfigHelper.class);
48
49     private OvsdbTepAddConfigHelper() {
50
51     }
52
53     /**
54      * Adds the TEP into ITM configuration/operational Datastore in one of the following cases.
55      * 1) default transport zone
56      * 2) Configured transport zone
57      * 3) Unhosted transport zone
58      *
59      * @param tepIp TEP-IP address in string
60      * @param strDpnId bridge datapath ID in string
61      * @param tzName transport zone name in string
62      * @param ofTunnel boolean flag for TEP to enable/disable of-tunnel feature on it
63      * @param dataBroker data broker handle to perform operations on config/operational datastore
64      * @param txRunner ManagedTransactionRunner object
65      */
66
67     public static List<? extends ListenableFuture<?>> addTepReceivedFromOvsdb(String tepIp, String strDpnId,
68                                                                               String tzName, boolean ofTunnel,
69                                                                               DataBroker dataBroker,
70                                                                               ManagedNewTransactionRunner txRunner)
71                                                                                       throws Exception {
72         Uint64 dpnId = Uint64.ZERO;
73
74         if (strDpnId != null && !strDpnId.isEmpty()) {
75             dpnId = MDSALUtil.getDpnId(strDpnId);
76         }
77
78         // Get tep IP
79         IpAddress tepIpAddress = IpAddressBuilder.getDefaultInstance(tepIp);
80         TransportZone tzone = null;
81
82         // check if TEP received is already present in any other TZ.
83         TransportZone transportZone = ItmUtils.getTransportZoneOfVtep(dpnId, dataBroker);
84         if (transportZone != null) {
85             LOG.trace("Vtep (tep-ip: {} and dpid: {}) is already present in transport-zone: {}",
86                     tepIpAddress, dpnId, transportZone.getZoneName());
87             if (!transportZone.getZoneName().equals(tzName)) {
88                 // remove TEP from TZ because TZ is updated for TEP from southbound in this case
89                 OvsdbTepRemoveWorker ovsdbTepRemoveWorkerObj = new OvsdbTepRemoveWorker(tepIp, strDpnId,
90                         transportZone.getZoneName(), dataBroker);
91                 ovsdbTepRemoveWorkerObj.call();
92             }
93         }
94
95         // Case: TZ name is not given with OVS TEP.
96         if (tzName == null) {
97             tzName = ITMConstants.DEFAULT_TRANSPORT_ZONE;
98             // add TEP into default-TZ
99             tzone = ItmUtils.getTransportZoneFromConfigDS(tzName, dataBroker);
100             if (tzone == null) {
101                 // Case: default-TZ is not yet created, then add TEP into "teps-in-not-hosted-transport-zone"
102                 LOG.trace("Adding TEP with default TZ into teps-in-not-hosted-transport-zone.");
103                 return addUnknownTzTepIntoTepsNotHostedAndReturnFutures(tzName, tepIpAddress, dpnId, ofTunnel,
104                         dataBroker, txRunner);
105             }
106             LOG.trace("Add TEP into default-transport-zone.");
107         } else {
108             // Case: Add TEP into corresponding TZ created from Northbound.
109             tzone = ItmUtils.getTransportZoneFromConfigDS(tzName, dataBroker);
110             if (tzone == null) {
111                 // Case: TZ is not configured from Northbound, then add TEP into "teps-in-not-hosted-transport-zone"
112                 LOG.trace("Adding TEP with unknown TZ into teps-in-not-hosted-transport-zone.");
113                 return addUnknownTzTepIntoTepsNotHostedAndReturnFutures(tzName, tepIpAddress, dpnId, ofTunnel,
114                         dataBroker, txRunner);
115             } else {
116                 LOG.trace("Add TEP into transport-zone already configured by Northbound.");
117             }
118         }
119
120         List<ListenableFuture<?>> futures = new ArrayList<>();
121         final Uint64 id = dpnId;
122         final String name = tzName;
123         futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
124             tx -> addConfig(name, id, tepIpAddress, ofTunnel, tx)));
125         return futures;
126     }
127
128     /**
129      * Adds the TEP into Vtep list in the subnet list in the transport zone list
130      * from ITM configuration Datastore by merge operation with write transaction.
131      *
132      * @param updatedVtepList updated Vteps list object which will have new TEP for addition
133      * @param tepIpAddress TEP IP address in IpAddress object
134      * @param tzName transport zone name in string
135      * @param dpid bridge datapath ID
136      * @param ofTunnel boolean flag for TEP to enable/disable of-tunnel feature on it
137      * @param tx TypedWriteTransaction object
138      */
139     public static void addVtepInITMConfigDS(List<Vteps> updatedVtepList, IpAddress tepIpAddress, String tzName,
140                                             Uint64 dpid, boolean ofTunnel,
141                                             TypedWriteTransaction<Datastore.Configuration> tx) {
142         //Create TZ node path
143         InstanceIdentifier<TransportZone> tranzportZonePath =
144                 InstanceIdentifier.builder(TransportZones.class)
145                         .child(TransportZone.class, new TransportZoneKey(tzName)).build();
146
147         // this check is needed to reuse same function from TransportZoneListener
148         // when VTEP is moved from TepsNotHosted list to TZ configured from Northbound.
149         if (dpid.compareTo(Uint64.ZERO) > 0) {
150             // create vtep
151             VtepsKey vtepkey = new VtepsKey(dpid);
152             Vteps vtepObj =
153                     new VtepsBuilder().setDpnId(dpid).setIpAddress(tepIpAddress).withKey(vtepkey)
154                             .setOptionOfTunnel(ofTunnel).build();
155
156             // Add vtep obtained from bridge into list
157             updatedVtepList.add(vtepObj);
158
159             LOG.trace("Adding TEP (TZ: {} TEP IP: {} DPID: {}, of-tunnel: {}) in ITM Config DS.", tzName,
160                     tepIpAddress, dpid, ofTunnel);
161         } else {
162             // this is case when this function is called while TEPs movement from tepsNotHosted list when
163             // corresponding TZ is configured from northbound.
164             for (Vteps vtep: updatedVtepList) {
165                 LOG.trace("Moving TEP (TEP IP: {} DPID: {}, of-tunnel: {})"
166                                 + "from not-hosted-transport-zone {} into  ITM Config DS.",
167                         vtep.getIpAddress(), vtep.getDpnId(), ofTunnel, tzName);
168             }
169         }
170
171         // create TZ node with updated subnet having new vtep
172         TransportZone updatedTzone =
173                 new TransportZoneBuilder().withKey(new TransportZoneKey(tzName)).setVteps(updatedVtepList)
174                         .setZoneName(tzName).build();
175
176         // Update TZ in Config DS to add vtep in TZ
177         tx.mergeParentStructureMerge(tranzportZonePath, updatedTzone);
178     }
179
180     /**
181      * Adds the TEP into Vtep list in the subnet list in the transport zone list
182      * from ITM operational Datastore by merge operation with write transaction.
183      *
184      * @param tzName transport zone name in string
185      * @param tepIpAddress TEP IP address in IpAddress object
186      * @param dpid bridge datapath ID
187      * @param ofTunnel boolean flag for TEP to enable/disable of-tunnel feature on it
188      * @param dataBroker data broker handle to perform operations on operational datastore
189      * @param tx TypedWriteTransaction object
190      */
191     protected static void addUnknownTzTepIntoTepsNotHosted(String tzName, IpAddress tepIpAddress,
192                                                            Uint64 dpid, boolean ofTunnel, DataBroker dataBroker,
193                                                            TypedWriteTransaction<Datastore.Operational> tx) {
194         Map<UnknownVtepsKey, UnknownVteps> vtepList;
195         TepsInNotHostedTransportZone tepsInNotHostedTransportZone =
196                 ItmUtils.getUnknownTransportZoneFromITMOperDS(tzName, dataBroker);
197         if (tepsInNotHostedTransportZone == null) {
198             LOG.trace("Unhosted TransportZone ({}) does not exist in OperDS.", tzName);
199             vtepList = new HashMap<>();
200             addVtepIntoTepsNotHosted(addVtepToUnknownVtepsList(vtepList, tepIpAddress, dpid, ofTunnel), tzName, tx);
201         } else {
202             vtepList = tepsInNotHostedTransportZone.getUnknownVteps();
203             if (vtepList == null || vtepList.isEmpty()) {
204                 //  case: vtep list does not exist or it has no elements
205                 if (vtepList == null) {
206                     LOG.trace("Add TEP into unhosted TZ ({}) when no vtep-list in the TZ.", tzName);
207                     vtepList = new HashMap<>();
208                 }
209                 LOG.trace("Add TEP into unhosted TZ ({}) when no vtep-list in the TZ.", tzName);
210                 addVtepIntoTepsNotHosted(addVtepToUnknownVtepsList(vtepList, tepIpAddress, dpid, ofTunnel), tzName, tx);
211             } else {
212                 //  case: vtep list has elements
213                 boolean vtepFound = false;
214                 UnknownVteps oldVtep = null;
215
216                 for (UnknownVteps vtep : vtepList.values()) {
217                     if (Objects.equals(vtep.getDpnId(), dpid)) {
218                         vtepFound = true;
219                         oldVtep = vtep;
220                         break;
221                     }
222                 }
223                 if (!vtepFound) {
224                     addVtepIntoTepsNotHosted(addVtepToUnknownVtepsList(vtepList,
225                             tepIpAddress, dpid, ofTunnel), tzName, tx);
226                 } else {
227                     // vtep is found, update it with tep-ip
228                     vtepList.remove(oldVtep.key());
229                     addVtepIntoTepsNotHosted(addVtepToUnknownVtepsList(vtepList,
230                             tepIpAddress, dpid, ofTunnel), tzName, tx);
231                 }
232             }
233         }
234     }
235
236     /**
237      * Adds the TEP into Unknown Vtep list under the transport zone in the TepsNotHosted list
238      * from ITM operational Datastore by merge operation with write transaction.
239      *
240      * @param updatedVtepList updated UnknownVteps list object which will have new TEP for addition
241      *                        into TepsNotHosted
242      * @param tzName transport zone name in string
243      * @param tx TypedWriteTransaction object
244      */
245     protected static void addVtepIntoTepsNotHosted(Map<UnknownVtepsKey, UnknownVteps> updatedVtepList, String tzName,
246                                                    TypedWriteTransaction<Datastore.Operational> tx) {
247         //Create TZ node path
248         InstanceIdentifier<TepsInNotHostedTransportZone> tepsInNotHostedTransportZoneIid =
249                 InstanceIdentifier.builder(NotHostedTransportZones.class)
250                         .child(TepsInNotHostedTransportZone.class,
251                                 new TepsInNotHostedTransportZoneKey(tzName)).build();
252
253         // create unknown TZ node with updated vtep list
254         TepsInNotHostedTransportZone updatedTzone = new TepsInNotHostedTransportZoneBuilder()
255                 .withKey(new TepsInNotHostedTransportZoneKey(tzName)).setZoneName(tzName)
256                 .setUnknownVteps(updatedVtepList).build();
257
258         // Update TZ in Oper DS.
259         tx.mergeParentStructureMerge(tepsInNotHostedTransportZoneIid, updatedTzone);
260     }
261
262     private static void addConfig(String tzName, Uint64 dpnId, IpAddress ipAdd,
263                                   boolean ofTunnel, TypedWriteTransaction<Datastore.Configuration> tx) {
264         List<Vteps> vtepList = new ArrayList<>();
265
266         LOG.trace("Add TEP in transport-zone when no vtep-list for specific subnet.");
267         addVtepInITMConfigDS(vtepList, ipAdd, tzName, dpnId, ofTunnel, tx);
268     }
269
270     private static List<? extends ListenableFuture<?>> addUnknownTzTepIntoTepsNotHostedAndReturnFutures(String tzName,
271                                                          IpAddress tepIpAddress, Uint64 id, boolean ofTunnel,
272                                                          DataBroker dataBroker, ManagedNewTransactionRunner txRunner) {
273         return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.OPERATIONAL,
274             tx -> addUnknownTzTepIntoTepsNotHosted(tzName, tepIpAddress, id, ofTunnel, dataBroker, tx)));
275     }
276
277     private static Map<UnknownVtepsKey, UnknownVteps> addVtepToUnknownVtepsList(Map<UnknownVtepsKey,
278             UnknownVteps> updatedVtepList, IpAddress tepIpAddress, Uint64 dpid, boolean ofTunnel) {
279         // create vtep
280         UnknownVtepsKey vtepkey = new UnknownVtepsKey(dpid);
281         UnknownVteps vtepObj =
282                 new UnknownVtepsBuilder().setDpnId(dpid).setIpAddress(tepIpAddress).withKey(vtepkey)
283                         .setOfTunnel(ofTunnel).build();
284
285         // Add vtep obtained into unknown TZ tep list
286         updatedVtepList.put(vtepObj.key(),vtepObj);
287         LOG.trace("Adding TEP  (DPID: {}, TEP IP: {}, of-tunnel: {}) into unhosted Transport Zone"
288                 + "inside ITM Oper DS.", dpid, tepIpAddress, ofTunnel);
289         return updatedVtepList;
290     }
291 }