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 java.util.HashSet;
12 import java.util.List;
13 import java.util.Map.Entry;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Future;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
22 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
24 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
27 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
28 import org.opendaylight.sfc.provider.SfcProviderRpc;
29 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
30 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopOutput;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.SubjectFeatureDefinitions;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubjectFeatureInstances;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
48 import org.opendaylight.yangtools.concepts.ListenerRegistration;
49 import org.opendaylight.yangtools.yang.binding.DataObject;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 import com.google.common.base.Optional;
56 import com.google.common.util.concurrent.FutureCallback;
57 import com.google.common.util.concurrent.Futures;
58 import com.google.common.util.concurrent.ListenableFuture;
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, DataChangeListener {
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<DataChangeListener> 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<String, RenderedServicePathFirstHop>();
121 * For now, listen to all changes in rules
123 allActionInstancesIid =
124 InstanceIdentifier.builder(Tenants.class)
126 .child(SubjectFeatureInstances.class)
127 .child(ActionInstance.class)
129 actionListener = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
130 allActionInstancesIid, this, DataChangeScope.ONE);
131 LOG.debug("SfcManager: Started");
134 public Set<IpAddress> getSfcSourceIps() {
135 if (rspMap.isEmpty()) return null;
137 Set<IpAddress> ipAddresses = new HashSet<IpAddress>();
138 for (RenderedServicePathFirstHop rsp: rspMap.values()) {
139 if (rsp.getIp() != null) {
140 ipAddresses.add(rsp.getIp());
143 if (ipAddresses.isEmpty()) return null;
148 public void onDataChanged(
149 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> actionInstanceNotification) {
151 for (DataObject dao : actionInstanceNotification.getCreatedData().values()) {
152 if (dao instanceof ActionInstance) {
153 ActionInstance ai = (ActionInstance)dao;
154 LOG.debug("New ActionInstance created");
155 executor.execute(new MatchActionDefTask(ai, null,
160 for (InstanceIdentifier<?> iid : actionInstanceNotification.getRemovedPaths()) {
161 DataObject old = actionInstanceNotification.getOriginalData().get(iid);
162 if (old instanceof ActionInstance) {
163 ActionInstance ai = (ActionInstance)old;
164 executor.execute(new MatchActionDefTask(null, ai,
165 ActionState.DELETE));
169 for (Entry<InstanceIdentifier<?>, DataObject> entry:
170 actionInstanceNotification.getUpdatedData().entrySet()) {
171 DataObject dao = entry.getValue();
172 if (dao instanceof ActionInstance) {
173 ActionInstance nai = (ActionInstance)dao;
174 ActionInstance oai = null;
175 InstanceIdentifier<?> iid = entry.getKey();
176 DataObject orig = actionInstanceNotification.getOriginalData().get(iid);
178 oai = (ActionInstance)orig;
180 * We may have some cleanup here. If the reference to
181 * the Action Definition changed, or if the Action Instance's
182 * chain parameter then we're no longer
183 * an action, and we may need to remove the RSP.
187 executor.execute(new MatchActionDefTask(nai, oai,
188 ActionState.CHANGE));
194 * Private internal class that gets the action definition
195 * referenced by the instance. If the definition has an
196 * action of "chain" (or whatever we decide to use
197 * here), then we need to invoke the SFC API to go
198 * get the chain information, which we'll eventually
199 * use during policy resolution.
202 private class MatchActionDefTask implements Runnable,
203 FutureCallback<Optional<ActionDefinition>> {
204 private final ActionState state;
205 private final ActionInstance actionInstance;
206 private final ActionInstance originalInstance;
207 private final InstanceIdentifier<ActionDefinition> adIid;
208 private final ActionDefinitionId id;
210 public MatchActionDefTask(ActionInstance actionInstance,
211 ActionInstance originalInstance, ActionState state) {
212 this.actionInstance = actionInstance;
213 this.originalInstance = originalInstance;
214 if (actionInstance != null) {
215 this.id = actionInstance.getActionDefinitionId();
221 adIid = InstanceIdentifier.builder(SubjectFeatureDefinitions.class)
222 .child(ActionDefinition.class,
223 new ActionDefinitionKey(this.id))
229 * Create read transaction with callback to look up
230 * the Action Definition that the Action Instance
235 ReadOnlyTransaction rot = dataBroker.newReadOnlyTransaction();
236 ListenableFuture<Optional<ActionDefinition>> dao =
237 rot.read(LogicalDatastoreType.OPERATIONAL, adIid);
238 Futures.addCallback(dao, this, executor);
243 public void onFailure(Throwable arg0) {
244 LOG.error("Failure reading ActionDefinition {}", id.getValue());
248 * An Action Definition exists - now we need to see
249 * if the Action Definition is for a chain action,
250 * and implement the appropriate behavior. If it's
251 * not a chain action, then we can ignore it.
256 public void onSuccess(Optional<ActionDefinition> dao) {
257 LOG.debug("Found ActionDefinition {}", id.getValue());
258 if (!dao.isPresent()) return;
260 ActionDefinition ad = dao.get();
261 if (ad.getId().getValue().equals(ChainActionDefinition.ID.getValue())) {
263 * We have the state we need:
264 * 1) it's a "CHAIN" action
265 * 2) the name is defined in the ActionInstance
270 * Go get the RSP First Hop
276 * We only care if the named chain changes
282 * If the instance is deleted, we need to remove
293 private ParameterValue getChainNameParameter(List<ParameterValue> pvl) {
294 if (pvl == null) return null;
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());
326 if (pv == null) return;
327 rspMap.remove(pv.getStringValue());
331 * Get the RenderedServicePathFirstHop from SFC
333 * TODO: what if SFC state isn't available at the time of
334 * this call, but becomes available later? Do we want
335 * or need some sort of notification handler for this?
337 private void addSfcRsp() {
339 getChainNameParameter(actionInstance.getParameterValue());
340 if (pv == null) return;
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());
375 if (pv == null) return;
377 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
378 SfcName chainName=new SfcName(pv.getStringValue());
379 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(chainName);
380 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
381 for(ServiceFunctionPath path: paths.getServiceFunctionPath()) {
382 if(path.getServiceChainName().equals(chainName)) {
383 LOG.info("Found path {} for chain {}",path.getName(),path.getServiceChainName());
390 * Return the first hop information for the Rendered Service Path
392 * @param rspName the Rendered Service Path
393 * @return the first hop information for the Rendered Service Path
395 public RenderedServicePathFirstHop getRspFirstHop(String rspName) {
396 return rspMap.get(rspName);
400 public void close() throws Exception {
401 if (actionListener != null) actionListener.close();