2 * Copyright (c) 2015 Intel, Cisco 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
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
11 import com.google.common.base.Optional;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import java.util.Collection;
16 import java.util.HashSet;
17 import java.util.List;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import java.util.concurrent.ExecutorService;
22 import java.util.concurrent.Future;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
31 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
32 import org.opendaylight.groupbasedpolicy.util.IetfModelCodec;
33 import org.opendaylight.sfc.provider.SfcProviderRpc;
34 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
35 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopOutput;
39 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
40 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
41 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
42 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.SubjectFeatureDefinitions;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
54 import org.opendaylight.yangtools.concepts.ListenerRegistration;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.opendaylight.yangtools.yang.common.RpcResult;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * Manage the state exchanged with SFC
63 * For the Proof of Concept, this manages the
64 * RenderedServicePathFirstHop elements that
65 * are retrieved from SFC.
68 public class SfcManager implements AutoCloseable, DataTreeChangeListener<ActionInstance> {
69 private static final Logger LOG =
70 LoggerFactory.getLogger(SfcManager.class);
72 private final DataBroker dataBroker;
73 private final ExecutorService executor;
74 private final InstanceIdentifier<ActionInstance> allActionInstancesIid;
75 private final ListenerRegistration<?> actionListener;
78 * local cache of the RSP first hops that we've requested from SFC,
81 private final ConcurrentMap<String, RenderedServicePathFirstHop> rspMap;
84 * TODO: these two String defs should move to the common
85 * "chain" action, once we have it.
88 public static final String SFC_CHAIN_ACTION = "chain";
89 // the parameter used for storing the chain name
90 public static final String SFC_CHAIN_NAME = "sfc-chain-name";
92 private static enum ActionState {
99 ActionState(String state) {
104 public String toString() {
110 public SfcManager(DataBroker dataBroker,
111 RpcProviderRegistry rpcRegistry,
112 ExecutorService executor) {
113 this.dataBroker = dataBroker;
114 this.executor = executor;
116 * Use thread-safe type only because we use an executor
118 this.rspMap = new ConcurrentHashMap<>();
121 * For now, listen to all changes in rules
123 allActionInstancesIid =
124 InstanceIdentifier.builder(Tenants.class)
127 .child(SubjectFeatureInstances.class)
128 .child(ActionInstance.class)
130 actionListener = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(
131 LogicalDatastoreType.CONFIGURATION, allActionInstancesIid), this);
132 LOG.debug("SfcManager: Started");
135 public Set<IpAddress> getSfcSourceIps() {
136 if (rspMap.isEmpty()) {
140 Set<IpAddress> ipAddresses = new HashSet<>();
141 for (RenderedServicePathFirstHop rsp: rspMap.values()) {
142 if (rsp.getIp() != null) {
143 ipAddresses.add(IetfModelCodec.ipAddress2010(rsp.getIp()));
146 if (ipAddresses.isEmpty()) {
153 public void onDataTreeChanged(Collection<DataTreeModification<ActionInstance>> changes) {
154 for (DataTreeModification<ActionInstance> change: changes) {
155 DataObjectModification<ActionInstance> rootNode = change.getRootNode();
156 final ActionInstance dataBefore = rootNode.getDataBefore();
157 final ActionInstance dataAfter = rootNode.getDataAfter();
158 switch (rootNode.getModificationType()) {
159 case SUBTREE_MODIFIED:
161 if (dataBefore == null) {
162 LOG.debug("New ActionInstance created");
163 executor.execute(new MatchActionDefTask(dataAfter, null, ActionState.ADD));
166 We may have some cleanup here. If the reference to
167 the Action Definition changed, or if the Action Instance's
168 chain parameter then we're no longer
169 an action, and we may need to remove the RSP.
171 LOG.debug("ActionInstance updated");
172 executor.execute(new MatchActionDefTask(dataAfter, dataBefore, ActionState.CHANGE));
176 LOG.debug("ActionInstance deleted");
177 executor.execute(new MatchActionDefTask(null, dataBefore, ActionState.DELETE));
186 * Private internal class that gets the action definition
187 * referenced by the instance. If the definition has an
188 * action of "chain" (or whatever we decide to use
189 * here), then we need to invoke the SFC API to go
190 * get the chain information, which we'll eventually
191 * use during policy resolution.
194 private class MatchActionDefTask implements Runnable,
195 FutureCallback<Optional<ActionDefinition>> {
196 private final ActionState state;
197 private final ActionInstance actionInstance;
198 private final ActionInstance originalInstance;
199 private final InstanceIdentifier<ActionDefinition> adIid;
200 private final ActionDefinitionId id;
202 public MatchActionDefTask(ActionInstance actionInstance,
203 ActionInstance originalInstance, ActionState state) {
204 this.actionInstance = actionInstance;
205 this.originalInstance = originalInstance;
206 if (actionInstance != null) {
207 this.id = actionInstance.getActionDefinitionId();
213 adIid = InstanceIdentifier.builder(SubjectFeatureDefinitions.class)
214 .child(ActionDefinition.class,
215 new ActionDefinitionKey(this.id))
221 * Create read transaction with callback to look up
222 * the Action Definition that the Action Instance
227 ReadOnlyTransaction rot = dataBroker.newReadOnlyTransaction();
228 ListenableFuture<Optional<ActionDefinition>> dao =
229 rot.read(LogicalDatastoreType.OPERATIONAL, adIid);
230 Futures.addCallback(dao, this, executor);
235 public void onFailure(Throwable arg0) {
236 LOG.error("Failure reading ActionDefinition {}", id.getValue());
240 * An Action Definition exists - now we need to see
241 * if the Action Definition is for a chain action,
242 * and implement the appropriate behavior. If it's
243 * not a chain action, then we can ignore it.
248 public void onSuccess(Optional<ActionDefinition> dao) {
249 LOG.debug("Found ActionDefinition {}", id.getValue());
250 if (!dao.isPresent()) {
254 ActionDefinition ad = dao.get();
255 if (ad.getId().getValue().equals(ChainActionDefinition.ID.getValue())) {
257 * We have the state we need:
258 * 1) it's a "CHAIN" action
259 * 2) the name is defined in the ActionInstance
264 * Go get the RSP First Hop
270 * We only care if the named chain changes
276 * If the instance is deleted, we need to remove
287 private ParameterValue getChainNameParameter(List<ParameterValue> pvl) {
291 for (ParameterValue pv: actionInstance.getParameterValue()) {
292 if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
299 private void changeSfcRsp() {
300 ParameterValue newPv =
301 getChainNameParameter(actionInstance.getParameterValue());
302 ParameterValue origPv =
303 getChainNameParameter(originalInstance.getParameterValue());
304 if (!newPv.getStringValue().equals(origPv.getStringValue())) {
305 if (rspMap.containsKey(origPv.getStringValue())) {
307 * Flow cleanup will happen as part of the
310 * TODO: can we guarantee that this
311 * happens after we remove the RSP?).
313 rspMap.remove(origPv.getStringValue());
319 private void deleteSfcRsp() {
321 getChainNameParameter(originalInstance.getParameterValue());
325 rspMap.remove(pv.getStringValue());
329 * Get the RenderedServicePathFirstHop from SFC
331 * TODO: what if SFC state isn't available at the time of
332 * this call, but becomes available later? Do we want
333 * or need some sort of notification handler for this?
335 private void addSfcRsp() {
337 getChainNameParameter(actionInstance.getParameterValue());
342 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
343 ReadRenderedServicePathFirstHopInputBuilder builder =
344 new ReadRenderedServicePathFirstHopInputBuilder()
345 .setName(pv.getStringValue());
347 Future<RpcResult<ReadRenderedServicePathFirstHopOutput>> result =
348 SfcProviderRpc.getSfcProviderRpc()
349 .readRenderedServicePathFirstHop(builder.build());
352 RpcResult<ReadRenderedServicePathFirstHopOutput> output =
354 if (output.isSuccessful()) {
355 LOG.trace("RPC for chain {} succeeded!", pv.getStringValue());
356 RenderedServicePathFirstHop rspFirstHop =
357 output.getResult().getRenderedServicePathFirstHop();
359 * We won't retry installation in the map
360 * because the presumption is it's either
361 * the same object or contain the same
364 rspMap.putIfAbsent(pv.getStringValue(), rspFirstHop);
366 } catch (Exception e) {
367 LOG.warn("Failed ReadRenderedServicePathFirstHop RPC: {}", e);
368 // TODO: proper exception handling
372 private void getSfcChain() {
374 getChainNameParameter(actionInstance.getParameterValue());
379 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
380 SfcName chainName=new SfcName(pv.getStringValue());
381 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(chainName);
382 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
383 for(ServiceFunctionPath path: paths.getServiceFunctionPath()) {
384 if(path.getServiceChainName().equals(chainName)) {
385 LOG.info("Found path {} for chain {}",path.getName(),path.getServiceChainName());
392 * Return the first hop information for the Rendered Service Path
394 * @param rspName the Rendered Service Path
395 * @return the first hop information for the Rendered Service Path
397 public RenderedServicePathFirstHop getRspFirstHop(String rspName) {
398 return rspMap.get(rspName);
402 public void close() throws Exception {
403 if (actionListener != null) {
404 actionListener.close();