NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / cloud-servicechain / impl / src / main / java / org / opendaylight / netvirt / cloudservicechain / listeners / VrfListener.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.netvirt.cloudservicechain.listeners;
9
10 import java.util.Optional;
11 import java.math.BigInteger;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.stream.Collectors;
16 import javax.annotation.PostConstruct;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.netvirt.cloudservicechain.utils.VpnPseudoPortCache;
25 import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * Listens for VrfEntry creations or removal with the purpose of including the
38  * new label in the LFIB (or removing it) pointing to the VpnPseudoPort.
39  *
40  */
41 @Singleton
42 public class VrfListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfListener> {
43
44     private static final Logger LOG = LoggerFactory.getLogger(VrfListener.class);
45     private final DataBroker broker;
46     private final IMdsalApiManager mdsalMgr;
47     private final VpnPseudoPortCache vpnPseudoPortCache;
48
49     @Inject
50     public VrfListener(DataBroker broker, IMdsalApiManager mdsalMgr, VpnPseudoPortCache vpnPseudoPortCache) {
51         this.broker = broker;
52         this.mdsalMgr = mdsalMgr;
53         this.vpnPseudoPortCache = vpnPseudoPortCache;
54     }
55
56     @Override
57     @PostConstruct
58     public void init() {
59         registerListener(LogicalDatastoreType.CONFIGURATION, broker);
60     }
61
62     @Override
63     protected InstanceIdentifier<VrfEntry> getWildCardPath() {
64         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
65     }
66
67
68     @Override
69     protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntryDeleted) {
70         LOG.debug("VrfEntry removed: id={}  vrfEntry=[ destination={}, route-paths=[{}]]",
71                   identifier, vrfEntryDeleted.getDestPrefix(), vrfEntryDeleted.getRoutePaths());
72         String vpnRd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
73         programLabelInAllVpnDpns(vpnRd, vrfEntryDeleted, NwConstants.DEL_FLOW);
74     }
75
76     @Override
77     protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
78         LOG.debug("VrfEntry updated: id={}  vrfEntry=[ destination={}, route-paths=[{}]]",
79                   identifier, update.getDestPrefix(), update.getRoutePaths());
80         List<Long> originalLabels = getUniqueLabelList(original);
81         List<Long> updateLabels = getUniqueLabelList(update);
82         if (!updateLabels.equals(originalLabels)) {
83             remove(identifier, original);
84             add(identifier, update);
85         }
86     }
87
88     @Override
89     protected void add(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntryAdded) {
90         LOG.debug("VrfEntry added: id={}  vrfEntry=[ destination={}, route-paths=[{}]]",
91                   identifier, vrfEntryAdded.getDestPrefix(), vrfEntryAdded.getRoutePaths());
92         String vpnRd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
93         programLabelInAllVpnDpns(vpnRd, vrfEntryAdded, NwConstants.ADD_FLOW);
94     }
95
96     /**
97      * Adds or Removes a VPN's route in all the DPNs where the VPN has footprint.
98      *
99      * @param vpnRd Route-Distinguisher of the VPN
100      * @param vrfEntry The route to add or remove
101      * @param addOrRemove States if the route must be added or removed
102      */
103     protected void programLabelInAllVpnDpns(String vpnRd, VrfEntry vrfEntry, int addOrRemove) {
104         Long vpnPseudoLPortTag = vpnPseudoPortCache.get(vpnRd);
105         if (vpnPseudoLPortTag == null) {
106             LOG.debug("Vpn with rd={} not related to any VpnPseudoPort", vpnRd);
107             return;
108         }
109
110         Optional<VpnInstanceOpDataEntry> vpnOpData = VpnServiceChainUtils.getVpnInstanceOpData(broker, vpnRd);
111         if (! vpnOpData.isPresent()) {
112             if (addOrRemove == NwConstants.ADD_FLOW) {
113                 LOG.error("VrfEntry added: Could not find operational data for VPN with RD={}", vpnRd);
114             } else {
115                 LOG.warn("VrfEntry removed: No Operational data found for VPN with RD={}. No further action", vpnRd);
116             }
117
118             return;
119         }
120
121         Collection<VpnToDpnList> vpnToDpnList = vpnOpData.get().getVpnToDpnList();
122         if (vpnToDpnList == null || vpnToDpnList.isEmpty()) {
123             LOG.warn("Empty VpnToDpnlist found in Operational for VPN with RD={}. No label will be {}",
124                      vpnRd, addOrRemove == NwConstants.ADD_FLOW ? "programmed" : "cleaned");
125             return;
126         }
127
128         for (VpnToDpnList dpnInVpn : vpnToDpnList) {
129             BigInteger dpnId = dpnInVpn.getDpnId();
130             VpnServiceChainUtils.programLFibEntriesForSCF(mdsalMgr, dpnId, Collections.singletonList(vrfEntry),
131                                                           (int) vpnPseudoLPortTag.longValue(), addOrRemove);
132         }
133     }
134
135     private List<Long> getUniqueLabelList(VrfEntry original) {
136         List<RoutePaths> vrfRoutePaths = original.getRoutePaths();
137         if (vrfRoutePaths == null || vrfRoutePaths.isEmpty()) {
138             return Collections.emptyList();
139         }
140
141         return vrfRoutePaths.stream()
142                             .filter(rPath -> rPath.getLabel() != null).map(RoutePaths::getLabel)
143                             .distinct().sorted().collect(Collectors.toList());
144     }
145
146     @Override
147     protected VrfListener getDataTreeChangeListener() {
148         return VrfListener.this;
149     }
150 }