76c3d714981a26e5970374c4d12091a558d47beb
[netvirt.git] / openstack / sfc-translator / impl / src / main / java / org / opendaylight / netvirt / openstack / sfc / translator / portchain / NeutronPortChainListener.java
1 /*
2  * Copyright (c) 2016 Brocade Communications Systems, Inc. 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.openstack.sfc.translator.portchain;
9
10 import com.google.common.util.concurrent.ThreadFactoryBuilder;
11 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
12 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
13 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
14 import org.opendaylight.netvirt.openstack.sfc.translator.DelegatingDataTreeListener;
15 import org.opendaylight.netvirt.openstack.sfc.translator.NeutronMdsalHelper;
16 import org.opendaylight.netvirt.openstack.sfc.translator.OvsdbMdsalHelper;
17 import org.opendaylight.netvirt.openstack.sfc.translator.OvsdbPortMetadata;
18 import org.opendaylight.netvirt.openstack.sfc.translator.SfcMdsalHelper;
19 import org.opendaylight.netvirt.openstack.sfc.translator.flowclassifier.FlowClassifierTranslator;
20 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
21 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathOutput;
22 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.DeleteRenderedPathInput;
23 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePathService;
24 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
25 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionBuilder;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifier;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortChains;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.chains.PortChain;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.ExecutorService;
51 import java.util.concurrent.Executors;
52 import java.util.concurrent.Future;
53 import java.util.concurrent.ThreadFactory;
54
55 /**
56  * OpenDaylight Neutron Port Chain yang models data change listener
57  */
58 public class NeutronPortChainListener extends DelegatingDataTreeListener<PortChain> {
59     private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChainListener.class);
60
61     private static final InstanceIdentifier<PortChain> portChainIid =
62             InstanceIdentifier.create(Neutron.class).child(PortChains.class).child(PortChain.class);
63     private final ExecutorService eventProcessor;
64     private final SfcMdsalHelper sfcMdsalHelper;
65     private final NeutronMdsalHelper neutronMdsalHelper;
66     private final OvsdbMdsalHelper ovsdbMdsalHelper;
67     private RenderedServicePathService rspService;
68
69     public NeutronPortChainListener(DataBroker db, RenderedServicePathService rspService) {
70         super(db,new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, portChainIid));
71         this.sfcMdsalHelper = new SfcMdsalHelper(db);
72         this.neutronMdsalHelper = new NeutronMdsalHelper(db);
73         this.ovsdbMdsalHelper = new OvsdbMdsalHelper(db);
74         this.rspService = rspService;
75         ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Port-Chain-Event-Processor").build();
76         this.eventProcessor = Executors.newSingleThreadExecutor(threadFactory);
77     }
78
79     /**
80      * Method removes PortChain which is identified by InstanceIdentifier.
81      *
82      * @param path - the whole path to PortChain
83      * @param deletedPortChain        - PortChain for removing
84      */
85     @Override
86     public void remove(InstanceIdentifier<PortChain> path, PortChain deletedPortChain) {
87         if(this.rspService != null) {
88             DeleteRenderedPathInput deleteRenderedPathInput =
89                     PortChainTranslator.buildDeleteRenderedServicePathInput(PortChainTranslator
90                     .getSFPKey(deletedPortChain));
91             if (deleteRenderedPathInput != null ) {
92                 this.rspService.deleteRenderedPath(deleteRenderedPathInput);
93             }
94         }
95         sfcMdsalHelper.deleteServiceFunctionPath(PortChainTranslator.getSFPKey(deletedPortChain));
96         sfcMdsalHelper.deleteServiceFunctionChain(PortChainTranslator.getSFCKey(deletedPortChain));
97     }
98
99     /**
100      * Method updates the original PortChain to the update PortChain.
101      * Both are identified by same InstanceIdentifier.
102      *
103      * @param path - the whole path to PortChain
104      * @param originalPortChain   - original PortChain (for update)
105      * @param updatePortChain     - changed PortChain (contain updates)
106      */
107     @Override
108     public void update(InstanceIdentifier<PortChain> path, PortChain originalPortChain, PortChain updatePortChain) {
109         //TODO: Add support for chain update
110     }
111
112     /**
113      * Method adds the PortChain which is identified by InstanceIdentifier
114      * to device.
115      *
116      * @param path - the whole path to new PortChain
117      * @param newPortChain        - new PortChain
118      */
119     @Override
120     public void add(final InstanceIdentifier<PortChain> path, final PortChain newPortChain) {
121         processPortChain(newPortChain);
122         eventProcessor.submit(() -> processPortChain(newPortChain));
123     }
124
125     private void processPortChain(PortChain newPortChain) {
126         //List of Port Pair Group attached to the Port Chain
127         List<PortPairGroup> portPairGroupList = new ArrayList<>();
128         //Port Pair Group and associated Port Pair
129         Map<Uuid, List<PortPair>> groupPortPairsList = new HashMap<>();
130         //Map of Port Pair uuid and Port pair ingress port related Neutron Port
131         Map<Uuid, Port> portPairToNeutronPortMap = new HashMap<>();
132
133         //Mapping of Port Pair UUID and OvsdbPortMetadata of the port pair ingress port
134         Map<Uuid, OvsdbPortMetadata> portPairOvsdbMetadata = new HashMap<>();
135
136         Map<Uuid, ServiceFunctionForwarderBuilder> portPairGroupToSFFMap = new HashMap<>();
137         List<ServiceFunction> portChainServiceFunctionList = new ArrayList<>();
138
139         //Read chain related port pair group, port pair and neutron port from neutron data store
140         for (Uuid ppgUuid : newPortChain.getPortPairGroups()) {
141             PortPairGroup ppg = neutronMdsalHelper.getNeutronPortPairGroup(ppgUuid);
142             if (ppg != null) {
143                 List<PortPair> portPairList = new ArrayList<>();
144                 portPairGroupList.add(ppg);
145                 for(Uuid ppUuid : ppg.getPortPairs()) {
146                     PortPair pp = neutronMdsalHelper.getNeutronPortPair(ppUuid);
147                     if (pp != null) {
148                         portPairList.add(pp);
149                         //NOTE:Assuming that ingress and egress port is same.
150                         Port neutronPort = neutronMdsalHelper.getNeutronPort(pp.getIngress());
151                         if (neutronPort != null) {
152                             portPairToNeutronPortMap.put(pp.getIngress(), neutronPort);
153                         }
154                     }
155                 }
156                 groupPortPairsList.put(ppgUuid, portPairList);
157             }
158         }
159
160         Topology ovsdbTopology = ovsdbMdsalHelper.getOvsdbTopologyTree();
161
162         //Read ovsdb port details related to neutron port. Each Port pair has two neutron port
163         //With the current implementation, i am assuming that we support SF only with single port
164         //that act as a ingress as well as egress.
165         for(Map.Entry<Uuid, Port> neutronPortEntry : portPairToNeutronPortMap.entrySet()) {
166             OvsdbPortMetadata ovsdbPortMetadata =
167                     ovsdbMdsalHelper.getOvsdbPortMetadata(
168                             neutronPortEntry.getValue().getKey().getUuid(),
169                             ovsdbTopology);
170
171             if(ovsdbPortMetadata != null) {
172                 portPairOvsdbMetadata.put(neutronPortEntry.getKey(), ovsdbPortMetadata);
173             }
174         }
175
176         //For each port pair group
177         for (PortPairGroup ppg : portPairGroupList) {
178             List<ServiceFunctionBuilder> portPairSFList = new ArrayList<>();
179
180             List<PortPair> portPairList =  groupPortPairsList.get(ppg.getUuid());
181             Map<Uuid, OvsdbPortMetadata> metadataList = new HashMap<>();
182             //Generate OvsdbPortMetadata for list of all the port pair
183             for (PortPair portPair : portPairList) {
184                 OvsdbPortMetadata metadata = portPairOvsdbMetadata.get(portPair.getIngress());
185
186                 if (metadata != null) {
187                     metadataList.put(portPair.getIngress(), metadata);
188                 }
189             }
190
191             //Build the SFF Builder from port pair group
192             ServiceFunctionForwarderBuilder sffBuilder =
193                     PortPairGroupTranslator.buildServiceFunctionForwarder(ppg,portPairList, metadataList);
194             LOG.info("SFF generated for Port Pair Group {} :: {}",ppg, sffBuilder);
195             //Check if SFF already exist
196             ServiceFunctionForwarder existingSff =
197                     sfcMdsalHelper.getExistingSFF(sffBuilder.getIpMgmtAddress().getIpv4Address().getValue());
198             if(existingSff != null) {
199                 LOG.info("SFF already exist for Port Pair Group {}. Existing SFF is {}",ppg, existingSff);
200                 sffBuilder = new ServiceFunctionForwarderBuilder(existingSff);
201             }
202             //Add SFF builder to the map for later reference
203             portPairGroupToSFFMap.put(ppg.getUuid(), sffBuilder);
204
205             //Generate all the SF and write it to SFC data store
206             for (PortPair portPair : portPairList) {
207                 OvsdbPortMetadata metadata = portPairOvsdbMetadata.get(portPair.getIngress());
208                 //Build the service function for the given port pair.
209                 ServiceFunctionBuilder sfBuilder = PortPairTranslator.buildServiceFunction(portPair,
210                         ppg,
211                         portPairToNeutronPortMap.get(portPair.getIngress()),
212                         metadata,
213                         sffBuilder.build());
214
215                 if (sfBuilder != null) {
216                     LOG.info("Service Function generated for the Port Pair {} :: {}", portPair, sfBuilder);
217                     //Write the Service Function to SFC data store.
218                     sfcMdsalHelper.addServiceFunction(sfBuilder.build());
219
220                     //Add to the list, to populated SFF Service Function Dictionary
221                     portPairSFList.add(sfBuilder);
222
223                     //Add the SF to Port Chain related SF list
224                     portChainServiceFunctionList.add(sfBuilder.build());
225                 } else {
226                     LOG.warn("Service Function building failed for Port Pair {}", portPair);
227                 }
228             }
229
230             //Update the Service Function Dictionary of SFF
231             for (ServiceFunctionBuilder sf : portPairSFList) {
232                 PortPairGroupTranslator.buildServiceFunctionDictonary(sffBuilder, sf.build());
233                 LOG.info("Updating Service Function dictionary of SFF {} for SF {}", sffBuilder, sf);
234             }
235             // Send SFF create request
236             LOG.info("Add Service Function Forwarder {} for Port Pair Group {}", sffBuilder.build(), ppg);
237             sfcMdsalHelper.addServiceFunctionForwarder(sffBuilder.build());
238         }
239         //Build Service Function Chain Builder
240         ServiceFunctionChain sfc =
241                 PortChainTranslator.buildServiceFunctionChain(newPortChain, portChainServiceFunctionList);
242
243         //Write SFC to data store
244         if (sfc != null) {
245             LOG.info("Add service function chain {}", sfc);
246             sfcMdsalHelper.addServiceFunctionChain(sfc);
247         } else {
248             LOG.warn("Service Function Chain building failed for Port Chain {}", newPortChain);
249         }
250
251         // Build Service Function Path Builder
252         ServiceFunctionPath sfp = PortChainTranslator.buildServiceFunctionPath(sfc);
253         //Write SFP to data store
254         if (sfp != null) {
255             LOG.info("Add service function path {}", sfp);
256            sfcMdsalHelper.addServiceFunctionPath(sfp);
257         } else {
258             LOG.warn("Service Function Path building failed for Service Chain {}", sfc);
259         }
260
261         //TODO:Generate Flow Classifiers and augment RSP on it.
262
263         if (this.rspService != null) {
264             // Build Create Rendered Service Path input
265             CreateRenderedPathInput rpInput = PortChainTranslator.buildCreateRenderedServicePathInput(sfp);
266
267             //Call Create Rendered Service Path RPC call
268             if (rpInput != null) {
269                 LOG.info("Call RPC for creating RSP :{}", rpInput);
270                 Future<RpcResult<CreateRenderedPathOutput>> result =  this.rspService.createRenderedPath(rpInput);
271                 try {
272                     if (result.get() != null) {
273                         CreateRenderedPathOutput output = result.get().getResult();
274                         LOG.debug("RSP name received from SFC : {}", output.getName());
275                         processFlowClassifiers(newPortChain, newPortChain.getFlowClassifiers(), output.getName());
276                     } else {
277                         LOG.error("RSP creation failed : {}", rpInput);
278                     }
279                 } catch (InterruptedException | ExecutionException e) {
280                     LOG.error("Error occurred during creating Rendered Service Path using RPC call", e);
281                 }
282             }
283         } else {
284             LOG.error("Rendered Path Service is not available, can't create Rendered Path for Port Chain", newPortChain);
285         }
286     }
287
288     private void processFlowClassifiers(PortChain pc, List<Uuid> flowClassifiers, String rspName) {
289         for (Uuid uuid : flowClassifiers) {
290             SfcFlowClassifier fc = neutronMdsalHelper.getNeutronFlowClassifier(uuid);
291             if (fc != null) {
292                 Acl acl = FlowClassifierTranslator.buildAcl(fc, rspName);
293                 if (acl != null ) {
294                     sfcMdsalHelper.addAclFlowClassifier(acl);
295                 } else {
296                     LOG.warn("Acl building failed for flow classifier {}. Traffic might not be redirected to RSP", fc);
297                 }
298
299             } else {
300                 LOG.error("Neutron Flow Classifier {} attached to Port Chain {} is not present in the neutron data " +
301                         "store", uuid, pc);
302             }
303         }
304     }
305 }