2 * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.openstack.sfc.translator.portchain;
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;
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.List;
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;
56 * OpenDaylight Neutron Port Chain yang models data change listener
58 public class NeutronPortChainListener extends DelegatingDataTreeListener<PortChain> {
59 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChainListener.class);
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;
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);
80 * Method removes PortChain which is identified by InstanceIdentifier.
82 * @param path - the whole path to PortChain
83 * @param deletedPortChain - PortChain for removing
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);
95 sfcMdsalHelper.deleteServiceFunctionPath(PortChainTranslator.getSFPKey(deletedPortChain));
96 sfcMdsalHelper.deleteServiceFunctionChain(PortChainTranslator.getSFCKey(deletedPortChain));
100 * Method updates the original PortChain to the update PortChain.
101 * Both are identified by same InstanceIdentifier.
103 * @param path - the whole path to PortChain
104 * @param originalPortChain - original PortChain (for update)
105 * @param updatePortChain - changed PortChain (contain updates)
108 public void update(InstanceIdentifier<PortChain> path, PortChain originalPortChain, PortChain updatePortChain) {
109 //TODO: Add support for chain update
113 * Method adds the PortChain which is identified by InstanceIdentifier
116 * @param path - the whole path to new PortChain
117 * @param newPortChain - new PortChain
120 public void add(final InstanceIdentifier<PortChain> path, final PortChain newPortChain) {
121 processPortChain(newPortChain);
122 eventProcessor.submit(() -> processPortChain(newPortChain));
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<>();
133 //Mapping of Port Pair UUID and OvsdbPortMetadata of the port pair ingress port
134 Map<Uuid, OvsdbPortMetadata> portPairOvsdbMetadata = new HashMap<>();
136 Map<Uuid, ServiceFunctionForwarderBuilder> portPairGroupToSFFMap = new HashMap<>();
137 List<ServiceFunction> portChainServiceFunctionList = new ArrayList<>();
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);
143 List<PortPair> portPairList = new ArrayList<>();
144 portPairGroupList.add(ppg);
145 for(Uuid ppUuid : ppg.getPortPairs()) {
146 PortPair pp = neutronMdsalHelper.getNeutronPortPair(ppUuid);
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);
156 groupPortPairsList.put(ppgUuid, portPairList);
160 Topology ovsdbTopology = ovsdbMdsalHelper.getOvsdbTopologyTree();
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(),
171 if(ovsdbPortMetadata != null) {
172 portPairOvsdbMetadata.put(neutronPortEntry.getKey(), ovsdbPortMetadata);
176 //For each port pair group
177 for (PortPairGroup ppg : portPairGroupList) {
178 List<ServiceFunctionBuilder> portPairSFList = new ArrayList<>();
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());
186 if (metadata != null) {
187 metadataList.put(portPair.getIngress(), metadata);
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);
202 //Add SFF builder to the map for later reference
203 portPairGroupToSFFMap.put(ppg.getUuid(), sffBuilder);
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,
211 portPairToNeutronPortMap.get(portPair.getIngress()),
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());
220 //Add to the list, to populated SFF Service Function Dictionary
221 portPairSFList.add(sfBuilder);
223 //Add the SF to Port Chain related SF list
224 portChainServiceFunctionList.add(sfBuilder.build());
226 LOG.warn("Service Function building failed for Port Pair {}", portPair);
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);
235 // Send SFF create request
236 LOG.info("Add Service Function Forwarder {} for Port Pair Group {}", sffBuilder.build(), ppg);
237 sfcMdsalHelper.addServiceFunctionForwarder(sffBuilder.build());
239 //Build Service Function Chain Builder
240 ServiceFunctionChain sfc =
241 PortChainTranslator.buildServiceFunctionChain(newPortChain, portChainServiceFunctionList);
243 //Write SFC to data store
245 LOG.info("Add service function chain {}", sfc);
246 sfcMdsalHelper.addServiceFunctionChain(sfc);
248 LOG.warn("Service Function Chain building failed for Port Chain {}", newPortChain);
251 // Build Service Function Path Builder
252 ServiceFunctionPath sfp = PortChainTranslator.buildServiceFunctionPath(sfc);
253 //Write SFP to data store
255 LOG.info("Add service function path {}", sfp);
256 sfcMdsalHelper.addServiceFunctionPath(sfp);
258 LOG.warn("Service Function Path building failed for Service Chain {}", sfc);
261 //TODO:Generate Flow Classifiers and augment RSP on it.
263 if (this.rspService != null) {
264 // Build Create Rendered Service Path input
265 CreateRenderedPathInput rpInput = PortChainTranslator.buildCreateRenderedServicePathInput(sfp);
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);
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());
277 LOG.error("RSP creation failed : {}", rpInput);
279 } catch (InterruptedException | ExecutionException e) {
280 LOG.error("Error occurred during creating Rendered Service Path using RPC call", e);
284 LOG.error("Rendered Path Service is not available, can't create Rendered Path for Port Chain", newPortChain);
288 private void processFlowClassifiers(PortChain pc, List<Uuid> flowClassifiers, String rspName) {
289 for (Uuid uuid : flowClassifiers) {
290 SfcFlowClassifier fc = neutronMdsalHelper.getNeutronFlowClassifier(uuid);
292 Acl acl = FlowClassifierTranslator.buildAcl(fc, rspName);
294 sfcMdsalHelper.addAclFlowClassifier(acl);
296 LOG.warn("Acl building failed for flow classifier {}. Traffic might not be redirected to RSP", fc);
300 LOG.error("Neutron Flow Classifier {} attached to Port Chain {} is not present in the neutron data " +