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.base.Preconditions;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.List;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.ExecutorService;
23 import java.util.concurrent.Future;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
29 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
32 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
33 import org.opendaylight.groupbasedpolicy.util.IetfModelCodec;
34 import org.opendaylight.sfc.provider.SfcProviderRpc;
35 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
36 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
38 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopOutput;
40 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
41 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
42 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
43 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.SubjectFeatureDefinitions;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Policy;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.SubjectFeatureInstances;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
55 import org.opendaylight.yangtools.concepts.ListenerRegistration;
56 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
57 import org.opendaylight.yangtools.yang.common.RpcResult;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * Manage the state exchanged with SFC
64 * For the Proof of Concept, this manages the
65 * RenderedServicePathFirstHop elements that
66 * are retrieved from SFC.
69 public class SfcManager implements AutoCloseable, DataTreeChangeListener<ActionInstance> {
70 private static final Logger LOG =
71 LoggerFactory.getLogger(SfcManager.class);
73 private final DataBroker dataBroker;
74 private final ExecutorService executor;
75 private final InstanceIdentifier<ActionInstance> allActionInstancesIid;
76 private final ListenerRegistration<?> actionListener;
77 private final SfcProviderRpc sfcProviderRpc;
80 * local cache of the RSP first hops that we've requested from SFC,
83 private final ConcurrentMap<String, RenderedServicePathFirstHop> rspMap;
86 * TODO: these two String defs should move to the common
87 * "chain" action, once we have it.
90 public static final String SFC_CHAIN_ACTION = "chain";
91 // the parameter used for storing the chain name
92 public static final String SFC_CHAIN_NAME = "sfc-chain-name";
94 private static enum ActionState {
101 ActionState(String state) {
106 public String toString() {
112 public SfcManager(DataBroker dataBroker,
113 RpcProviderRegistry rpcRegistry,
114 ExecutorService executor) {
115 Preconditions.checkNotNull(dataBroker, "Databroker for SfcManager must not be null!");
116 this.dataBroker = dataBroker;
117 this.executor = executor;
118 this.sfcProviderRpc = new SfcProviderRpc(dataBroker);
120 * Use thread-safe type only because we use an executor
122 this.rspMap = new ConcurrentHashMap<>();
125 * For now, listen to all changes in rules
127 allActionInstancesIid =
128 InstanceIdentifier.builder(Tenants.class)
131 .child(SubjectFeatureInstances.class)
132 .child(ActionInstance.class)
134 actionListener = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(
135 LogicalDatastoreType.CONFIGURATION, allActionInstancesIid), this);
136 LOG.debug("SfcManager: Started");
139 public Set<IpAddress> getSfcSourceIps() {
140 if (rspMap.isEmpty()) {
144 Set<IpAddress> ipAddresses = new HashSet<>();
145 for (RenderedServicePathFirstHop rsp: rspMap.values()) {
146 if (rsp.getIp() != null) {
147 ipAddresses.add(IetfModelCodec.ipAddress2010(rsp.getIp()));
150 if (ipAddresses.isEmpty()) {
157 public void onDataTreeChanged(Collection<DataTreeModification<ActionInstance>> changes) {
158 for (DataTreeModification<ActionInstance> change: changes) {
159 DataObjectModification<ActionInstance> rootNode = change.getRootNode();
160 final ActionInstance dataBefore = rootNode.getDataBefore();
161 final ActionInstance dataAfter = rootNode.getDataAfter();
162 switch (rootNode.getModificationType()) {
163 case SUBTREE_MODIFIED:
165 if (dataBefore == null) {
166 LOG.debug("New ActionInstance created");
167 executor.execute(new MatchActionDefTask(dataAfter, null, ActionState.ADD));
170 We may have some cleanup here. If the reference to
171 the Action Definition changed, or if the Action Instance's
172 chain parameter then we're no longer
173 an action, and we may need to remove the RSP.
175 LOG.debug("ActionInstance updated");
176 executor.execute(new MatchActionDefTask(dataAfter, dataBefore, ActionState.CHANGE));
180 LOG.debug("ActionInstance deleted");
181 executor.execute(new MatchActionDefTask(null, dataBefore, ActionState.DELETE));
190 * Private internal class that gets the action definition
191 * referenced by the instance. If the definition has an
192 * action of "chain" (or whatever we decide to use
193 * here), then we need to invoke the SFC API to go
194 * get the chain information, which we'll eventually
195 * use during policy resolution.
198 private class MatchActionDefTask implements Runnable,
199 FutureCallback<Optional<ActionDefinition>> {
200 private final ActionState state;
201 private final ActionInstance actionInstance;
202 private final ActionInstance originalInstance;
203 private final InstanceIdentifier<ActionDefinition> adIid;
204 private final ActionDefinitionId id;
206 public MatchActionDefTask(ActionInstance actionInstance,
207 ActionInstance originalInstance, ActionState state) {
208 this.actionInstance = actionInstance;
209 this.originalInstance = originalInstance;
210 if (actionInstance != null) {
211 this.id = actionInstance.getActionDefinitionId();
217 adIid = InstanceIdentifier.builder(SubjectFeatureDefinitions.class)
218 .child(ActionDefinition.class,
219 new ActionDefinitionKey(this.id))
225 * Create read transaction with callback to look up
226 * the Action Definition that the Action Instance
231 ReadOnlyTransaction rot = dataBroker.newReadOnlyTransaction();
232 ListenableFuture<Optional<ActionDefinition>> dao =
233 rot.read(LogicalDatastoreType.OPERATIONAL, adIid);
234 Futures.addCallback(dao, this, executor);
239 public void onFailure(Throwable arg0) {
240 LOG.error("Failure reading ActionDefinition {}", id.getValue());
244 * An Action Definition exists - now we need to see
245 * if the Action Definition is for a chain action,
246 * and implement the appropriate behavior. If it's
247 * not a chain action, then we can ignore it.
252 public void onSuccess(Optional<ActionDefinition> dao) {
253 LOG.debug("Found ActionDefinition {}", id.getValue());
254 if (!dao.isPresent()) {
258 ActionDefinition ad = dao.get();
259 if (ad.getId().getValue().equals(ChainActionDefinition.ID.getValue())) {
261 * We have the state we need:
262 * 1) it's a "CHAIN" action
263 * 2) the name is defined in the ActionInstance
268 * Go get the RSP First Hop
274 * We only care if the named chain changes
280 * If the instance is deleted, we need to remove
291 private ParameterValue getChainNameParameter(List<ParameterValue> pvl) {
295 for (ParameterValue pv: actionInstance.getParameterValue()) {
296 if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
303 private void changeSfcRsp() {
304 ParameterValue newPv =
305 getChainNameParameter(actionInstance.getParameterValue());
306 ParameterValue origPv =
307 getChainNameParameter(originalInstance.getParameterValue());
308 if (!newPv.getStringValue().equals(origPv.getStringValue())) {
309 if (rspMap.containsKey(origPv.getStringValue())) {
311 * Flow cleanup will happen as part of the
314 * TODO: can we guarantee that this
315 * happens after we remove the RSP?).
317 rspMap.remove(origPv.getStringValue());
323 private void deleteSfcRsp() {
325 getChainNameParameter(originalInstance.getParameterValue());
329 rspMap.remove(pv.getStringValue());
333 * Get the RenderedServicePathFirstHop from SFC
335 * TODO: what if SFC state isn't available at the time of
336 * this call, but becomes available later? Do we want
337 * or need some sort of notification handler for this?
339 private void addSfcRsp() {
341 getChainNameParameter(actionInstance.getParameterValue());
346 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
347 ReadRenderedServicePathFirstHopInputBuilder builder =
348 new ReadRenderedServicePathFirstHopInputBuilder()
349 .setName(pv.getStringValue());
351 Future<RpcResult<ReadRenderedServicePathFirstHopOutput>> result =
352 sfcProviderRpc.readRenderedServicePathFirstHop(builder.build());
355 RpcResult<ReadRenderedServicePathFirstHopOutput> output =
357 if (output.isSuccessful()) {
358 LOG.trace("RPC for chain {} succeeded!", pv.getStringValue());
359 RenderedServicePathFirstHop rspFirstHop =
360 output.getResult().getRenderedServicePathFirstHop();
362 * We won't retry installation in the map
363 * because the presumption is it's either
364 * the same object or contain the same
367 rspMap.putIfAbsent(pv.getStringValue(), rspFirstHop);
369 } catch (Exception e) {
370 LOG.warn("Failed ReadRenderedServicePathFirstHop RPC: {}", e);
371 // TODO: proper exception handling
375 private void getSfcChain() {
377 getChainNameParameter(actionInstance.getParameterValue());
382 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
383 SfcName chainName=new SfcName(pv.getStringValue());
384 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(chainName);
385 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
386 for(ServiceFunctionPath path: paths.getServiceFunctionPath()) {
387 if(path.getServiceChainName().equals(chainName)) {
388 LOG.info("Found path {} for chain {}",path.getName(),path.getServiceChainName());
395 * Return the first hop information for the Rendered Service Path
397 * @param rspName the Rendered Service Path
398 * @return the first hop information for the Rendered Service Path
400 public RenderedServicePathFirstHop getRspFirstHop(String rspName) {
401 return rspMap.get(rspName);
405 public void close() throws Exception {
406 if (actionListener != null) {
407 actionListener.close();