1 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay;
3 import java.util.HashSet;
5 import java.util.Map.Entry;
7 import java.util.concurrent.ConcurrentHashMap;
8 import java.util.concurrent.ConcurrentMap;
9 import java.util.concurrent.ExecutorService;
10 import java.util.concurrent.Future;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
14 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
15 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
19 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ChainAction;
20 import org.opendaylight.groupbasedpolicy.resolver.PolicyResolver;
21 import org.opendaylight.sfc.provider.SfcProviderRpc;
22 import org.opendaylight.sfc.provider.api.SfcProviderServiceChainAPI;
23 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
24 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopInputBuilder;
25 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.ReadRenderedServicePathFirstHopOutput;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
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.inet.types.rev100924.IpAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionDefinitionId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.SubjectFeatureDefinitions;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinition;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ActionDefinitionKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubjectFeatureInstances;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
40 import org.opendaylight.yangtools.concepts.ListenerRegistration;
41 import org.opendaylight.yangtools.yang.binding.DataObject;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.opendaylight.yangtools.yang.common.RpcResult;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.google.common.base.Optional;
48 import com.google.common.util.concurrent.FutureCallback;
49 import com.google.common.util.concurrent.Futures;
50 import com.google.common.util.concurrent.ListenableFuture;
53 * Manage the state exchanged with SFC
55 * For the Proof of Concept, this manages the
56 * RenderedServicePathFirstHop elements that
57 * are retrieved from SFC.
60 public class SfcManager implements AutoCloseable, DataChangeListener {
61 private static final Logger LOG =
62 LoggerFactory.getLogger(SfcManager.class);
64 private final DataBroker dataBroker;
65 private final ExecutorService executor;
66 private final InstanceIdentifier<ActionInstance> allActionInstancesIid;
67 private final ListenerRegistration<DataChangeListener> actionListener;
70 * local cache of the RSP first hops that we've requested from SFC,
73 private final ConcurrentMap<String, RenderedServicePathFirstHop> rspMap;
76 * TODO: these two String defs should move to the common
77 * "chain" action, once we have it.
80 public static final String SFC_CHAIN_ACTION = "chain";
81 // the parameter used for storing the chain name
82 public static final String SFC_CHAIN_NAME = "sfc-chain-name";
84 private static enum ActionState {
91 ActionState(String state) {
96 public String toString() {
102 public SfcManager(DataBroker dataBroker,
103 PolicyResolver policyResolver,
104 RpcProviderRegistry rpcRegistry,
105 ExecutorService executor) {
106 this.dataBroker = dataBroker;
107 this.executor = executor;
109 * Use thread-safe type only because we use an executor
111 this.rspMap = new ConcurrentHashMap<String, RenderedServicePathFirstHop>();
114 * For now, listen to all changes in rules
116 allActionInstancesIid =
117 InstanceIdentifier.builder(Tenants.class)
119 .child(SubjectFeatureInstances.class)
120 .child(ActionInstance.class)
122 actionListener = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
123 allActionInstancesIid, this, DataChangeScope.ONE);
124 LOG.debug("SfcManager: Started");
127 public Set<IpAddress> getSfcSourceIps() {
128 if (rspMap.isEmpty()) return null;
130 Set<IpAddress> ipAddresses = new HashSet<IpAddress>();
131 for (RenderedServicePathFirstHop rsp: rspMap.values()) {
132 if (rsp.getIp() != null) {
133 ipAddresses.add(rsp.getIp());
136 if (ipAddresses.isEmpty()) return null;
141 public void onDataChanged(
142 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> actionInstanceNotification) {
144 for (DataObject dao : actionInstanceNotification.getCreatedData().values()) {
145 if (dao instanceof ActionInstance) {
146 ActionInstance ai = (ActionInstance)dao;
147 LOG.debug("New ActionInstance created");
148 executor.execute(new MatchActionDefTask(ai, null,
153 for (InstanceIdentifier<?> iid : actionInstanceNotification.getRemovedPaths()) {
154 DataObject old = actionInstanceNotification.getOriginalData().get(iid);
155 if (old instanceof ActionInstance) {
156 ActionInstance ai = (ActionInstance)old;
157 executor.execute(new MatchActionDefTask(null, ai,
158 ActionState.DELETE));
162 for (Entry<InstanceIdentifier<?>, DataObject> entry:
163 actionInstanceNotification.getUpdatedData().entrySet()) {
164 DataObject dao = entry.getValue();
165 if (dao instanceof ActionInstance) {
166 ActionInstance nai = (ActionInstance)dao;
167 ActionInstance oai = null;
168 InstanceIdentifier<?> iid = entry.getKey();
169 DataObject orig = actionInstanceNotification.getOriginalData().get(iid);
171 oai = (ActionInstance)orig;
173 * We may have some cleanup here. If the reference to
174 * the Action Definition changed, or if the Action Instance's
175 * chain parameter then we're no longer
176 * an action, and we may need to remove the RSP.
180 executor.execute(new MatchActionDefTask(nai, oai,
181 ActionState.CHANGE));
187 * Private internal class that gets the action definition
188 * referenced by the instance. If the definition has an
189 * action of "chain" (or whatever we decide to use
190 * here), then we need to invoke the SFC API to go
191 * get the chain information, which we'll eventually
192 * use during policy resolution.
195 private class MatchActionDefTask implements Runnable,
196 FutureCallback<Optional<ActionDefinition>> {
197 private final ActionState state;
198 private final ActionInstance actionInstance;
199 private final ActionInstance originalInstance;
200 private final InstanceIdentifier<ActionDefinition> adIid;
201 private final ActionDefinitionId id;
203 public MatchActionDefTask(ActionInstance actionInstance,
204 ActionInstance originalInstance, ActionState state) {
205 this.actionInstance = actionInstance;
206 this.originalInstance = originalInstance;
207 if (actionInstance != null) {
208 this.id = actionInstance.getActionDefinitionId();
214 adIid = InstanceIdentifier.builder(SubjectFeatureDefinitions.class)
215 .child(ActionDefinition.class,
216 new ActionDefinitionKey(this.id))
222 * Create read transaction with callback to look up
223 * the Action Definition that the Action Instance
228 ReadOnlyTransaction rot = dataBroker.newReadOnlyTransaction();
229 ListenableFuture<Optional<ActionDefinition>> dao =
230 rot.read(LogicalDatastoreType.OPERATIONAL, adIid);
231 Futures.addCallback(dao, this, executor);
236 public void onFailure(Throwable arg0) {
237 LOG.error("Failure reading ActionDefinition {}", id.getValue());
241 * An Action Definition exists - now we need to see
242 * if the Action Definition is for a chain action,
243 * and implement the appropriate behavior. If it's
244 * not a chain action, then we can ignore it.
249 public void onSuccess(Optional<ActionDefinition> dao) {
250 LOG.debug("Found ActionDefinition {}", id.getValue());
251 if (!dao.isPresent()) return;
253 ActionDefinition ad = dao.get();
254 if (ad.getId().getValue().equals(ChainAction.ID.getValue())) {
256 * We have the state we need:
257 * 1) it's a "CHAIN" action
258 * 2) the name is defined in the ActionInstance
263 * Go get the RSP First Hop
269 * We only care if the named chain changes
275 * If the instance is deleted, we need to remove
286 private ParameterValue getChainNameParameter(List<ParameterValue> pvl) {
287 if (pvl == null) return null;
288 for (ParameterValue pv: actionInstance.getParameterValue()) {
289 if (pv.getName().getValue().equals(SFC_CHAIN_NAME)) {
296 private void changeSfcRsp() {
297 ParameterValue newPv =
298 getChainNameParameter(actionInstance.getParameterValue());
299 ParameterValue origPv =
300 getChainNameParameter(originalInstance.getParameterValue());
301 if (!newPv.getStringValue().equals(origPv.getStringValue())) {
302 if (rspMap.containsKey(origPv.getStringValue())) {
304 * Flow cleanup will happen as part of the
307 * TODO: can we guarantee that this
308 * happens after we remove the RSP?).
310 rspMap.remove(origPv.getStringValue());
316 private void deleteSfcRsp() {
318 getChainNameParameter(originalInstance.getParameterValue());
319 if (pv == null) return;
320 rspMap.remove(pv.getStringValue());
324 * Get the RenderedServicePathFirstHop from SFC
326 * TODO: what if SFC state isn't available at the time of
327 * this call, but becomes available later? Do we want
328 * or need some sort of notification handler for this?
330 private void addSfcRsp() {
332 getChainNameParameter(actionInstance.getParameterValue());
333 if (pv == null) return;
335 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
336 ReadRenderedServicePathFirstHopInputBuilder builder =
337 new ReadRenderedServicePathFirstHopInputBuilder()
338 .setName(pv.getStringValue());
340 Future<RpcResult<ReadRenderedServicePathFirstHopOutput>> result =
341 SfcProviderRpc.getSfcProviderRpc()
342 .readRenderedServicePathFirstHop(builder.build());
345 RpcResult<ReadRenderedServicePathFirstHopOutput> output =
347 if (output.isSuccessful()) {
348 LOG.trace("RPC for chain {} succeeded!", pv.getStringValue());
349 RenderedServicePathFirstHop rspFirstHop =
350 output.getResult().getRenderedServicePathFirstHop();
352 * We won't retry installation in the map
353 * because the presumption is it's either
354 * the same object or contain the same
357 rspMap.putIfAbsent(pv.getStringValue(), rspFirstHop);
359 } catch (Exception e) {
360 LOG.warn("Failed ReadRenderedServicePathFirstHop RPC: {}", e);
361 // TODO: proper exception handling
365 private void getSfcChain() {
367 getChainNameParameter(actionInstance.getParameterValue());
368 if (pv == null) return;
370 LOG.trace("Invoking RPC for chain {}", pv.getStringValue());
371 String chainName=pv.getStringValue();
372 ServiceFunctionChain chain = SfcProviderServiceChainAPI.readServiceFunctionChain(pv.getStringValue());
373 ServiceFunctionPaths paths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
374 for(ServiceFunctionPath path: paths.getServiceFunctionPath()) {
375 if(path.getServiceChainName().equals(chainName)) {
376 LOG.info("Found path {} for chain {}",path.getName(),path.getServiceChainName());
383 * Return the first hop information for the Rendered Service Path
388 public RenderedServicePathFirstHop getRspFirstHop(String rspName) {
389 return rspMap.get(rspName);
393 public void close() throws Exception {
394 if (actionListener != null) actionListener.close();