Bug 6778 - VPN interface for external port is deleted when clearing router gw interface
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / ExternalNetworkListener.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.natservice.internal;
9
10 import com.google.common.base.Optional;
11 import java.math.BigInteger;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
18 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
21 import org.opendaylight.genius.mdsalutil.FlowEntity;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.InstructionType;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchFieldType;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
28 import org.opendaylight.genius.mdsalutil.NwConstants;
29 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
35 import org.opendaylight.yangtools.concepts.ListenerRegistration;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 public class ExternalNetworkListener extends AbstractDataChangeListener<Networks> implements AutoCloseable {
41     private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkListener.class);
42     private ListenerRegistration<DataChangeListener> listenerRegistration;
43     private final DataBroker dataBroker;
44     private final IMdsalApiManager mdsalManager;
45
46     public ExternalNetworkListener (final DataBroker dataBroker, final IMdsalApiManager mdsalManager) {
47         super(Networks.class);
48         this.dataBroker = dataBroker;
49         this.mdsalManager = mdsalManager;
50     }
51
52     public void init() {
53         LOG.info("{} init", getClass().getSimpleName());
54         listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
55                 getWildCardPath(), this, AsyncDataBroker.DataChangeScope.SUBTREE);
56     }
57
58     private InstanceIdentifier<Networks> getWildCardPath() {
59         return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class);
60     }
61
62     @Override
63     public void close() throws Exception {
64         if (listenerRegistration != null) {
65             listenerRegistration.close();
66             listenerRegistration = null;
67         }
68         LOG.info("{} close", getClass().getSimpleName());
69     }
70
71     @Override
72     protected void add(final InstanceIdentifier<Networks> identifier,
73                        final Networks nw) {
74         LOG.trace("External Network add mapping method - key: " + identifier + ", value=" + nw );
75         processExternalNwAdd(identifier, nw);
76     }
77
78     @Override
79     protected void remove(InstanceIdentifier<Networks> identifier, Networks nw) {
80         LOG.trace("External Network remove mapping method - key: " + identifier + ", value=" + nw );
81         processExternalNwDel(identifier, nw);
82     }
83
84     @Override
85     protected void update(InstanceIdentifier<Networks> identifier, Networks original, Networks update) {
86         LOG.trace("External Network update mapping method - key: " + identifier + ", original=" + original + ", update=" + update );
87         //check if a new router has been added or an already existing router has been deleted from the external nw to router association
88         List<Uuid> oldRtrs = original.getRouterIds();
89         List<Uuid> newRtrs = update.getRouterIds();
90         if (oldRtrs != newRtrs) {
91             //handle both addition and removal of routers
92             for (Uuid rtr : newRtrs) {
93                 if (oldRtrs.contains(rtr)) {
94                     oldRtrs.remove(rtr);
95                 } else {
96                     // new router case
97                     //Routers added need to have the corresponding default Fib entry added to the switches in the router
98                     String routerId = rtr.getValue();
99                     addOrDelDefFibRouteToSNAT(routerId, true);
100
101                 }
102             }
103
104             //Routers removed need to have the corresponding default Fib entry removed from the switches in the router
105             for (Uuid rtr : oldRtrs) {
106                 String routerId = rtr.getValue();
107                 addOrDelDefFibRouteToSNAT(routerId, false);
108             }
109         }
110     }
111
112     private void processExternalNwAdd(final InstanceIdentifier<Networks> identifier,
113                                       final Networks network) {
114         LOG.trace("Add event - key: {}, value: {}", identifier, network);
115         List<Uuid> routerList = network.getRouterIds();
116
117         if(routerList == null) {
118             LOG.debug("No routers associated with external network {}", identifier);
119             return;
120         }
121
122         for(Uuid router: routerList) {
123             String routerId = router.getValue();
124             addOrDelDefFibRouteToSNAT(routerId, true);
125         }
126     }
127
128     private void processExternalNwDel(final InstanceIdentifier<Networks> identifier,
129                                       final Networks network) {
130         LOG.trace("Add event - key: {}, value: {}", identifier, network);
131         List<Uuid> routerList = network.getRouterIds();
132
133         for(Uuid router: routerList) {
134             String routerId = router.getValue();
135             addOrDelDefFibRouteToSNAT(routerId, false);
136         }
137     }
138
139     private void addOrDelDefFibRouteToSNAT(String routerId, boolean create) {
140         //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore
141         InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerId);
142         Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
143         if (vpnInstOp.isPresent()) {
144             List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
145             if (dpnListInVpn != null) {
146                 for (VpnToDpnList dpn : dpnListInVpn) {
147                     BigInteger dpnId = dpn.getDpnId();
148                     long vpnId = NatUtil.readVpnId(dataBroker, vpnInstOp.get().getVrfId());
149                     if (create) {
150                         installDefNATRouteInDPN(dpnId, vpnId);
151                     } else {
152                         removeDefNATRouteInDPN(dpnId, vpnId);
153                     }
154                 }
155             }
156         }
157     }
158
159     private FlowEntity buildDefNATFlowEntity(BigInteger dpId, long vpnId) {
160
161         InetAddress defaultIP = null;
162
163         try {
164             defaultIP = InetAddress.getByName("0.0.0.0");
165
166         } catch (UnknownHostException e) {
167             LOG.error("UnknowHostException in buildDefNATFlowEntity. Failed  to build FIB Table Flow for Default Route to NAT table ");
168             return null;
169         }
170
171         List<MatchInfo> matches = new ArrayList<>();
172         matches.add(new MatchInfo(MatchFieldType.eth_type,
173                 new long[] { 0x0800L }));
174
175         //add match for default route "0.0.0.0/0"
176         //matches.add(new MatchInfo(MatchFieldType.ipv4_src, new long[] {
177         //        NatUtil.getIpAddress(defaultIP.getAddress()), 0 }));
178
179         //add match for vrfid
180         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
181                 MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
182
183         List<InstructionInfo> instructions = new ArrayList<>();
184         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.PSNAT_TABLE }));
185
186         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.L3_FIB_TABLE, defaultIP);
187
188         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef,
189                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
190                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
191
192         return flowEntity;
193
194
195     }
196
197     private void installDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
198         FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
199         if(flowEntity == null) {
200             LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
201             return;
202         }
203         mdsalManager.installFlow(flowEntity);
204     }
205
206     private void removeDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
207         FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
208         if(flowEntity == null) {
209             LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
210             return;
211         }
212         mdsalManager.removeFlow(flowEntity);
213     }
214 }