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