b1d6e3ccc6ebac7292ab4d9a7c99b3a343359fee
[groupbasedpolicy.git] / renderers / ios-xe / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ios_xe_provider / impl / manager / PolicyManagerImpl.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager;
10
11 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Create;
12 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.DsAction.Delete;
13
14 import com.google.common.base.Function;
15 import com.google.common.base.Preconditions;
16 import com.google.common.util.concurrent.AsyncFunction;
17 import com.google.common.util.concurrent.CheckedFuture;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Optional;
23 import java.util.concurrent.TimeUnit;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
30 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.api.manager.PolicyManager;
31 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil;
32 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.RendererPolicyUtil;
33 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.StatusUtil;
34 import org.opendaylight.groupbasedpolicy.sxp.ep.provider.api.EPToSgtMapper;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.RendererName;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.Renderers;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.RendererKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicy;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicyBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.Configuration;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.Status;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.StatusBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpoint;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.status.UnconfiguredEndpointsBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.IpSgtDistributionService;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.ip.sgt.distribution.rev160715.SendIpSgtBindingToPeerInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 public class PolicyManagerImpl implements PolicyManager {
57
58     private static final Logger LOG = LoggerFactory.getLogger(PolicyManagerImpl.class);
59     public static final RendererName IOS_XE_RENDERER = new RendererName("ios-xe-renderer");
60     private static final String BASE_POLICY_MAP_NAME = "service-chains-";
61     private final DataBroker dataBroker;
62     private final NodeManager nodeManager;
63     private final EPToSgtMapper epToSgtMapper;
64     private final IpSgtDistributionService ipSgtDistributor;
65
66     public PolicyManagerImpl(final DataBroker dataBroker,
67                              final NodeManager nodeManager, final EPToSgtMapper epToSgtMapper,
68                              final IpSgtDistributionService ipSgtDistributor) {
69         this.dataBroker = Preconditions.checkNotNull(dataBroker);
70         this.nodeManager = Preconditions.checkNotNull(nodeManager);
71         this.epToSgtMapper = Preconditions.checkNotNull(epToSgtMapper);
72         this.ipSgtDistributor = Preconditions.checkNotNull(ipSgtDistributor);
73     }
74
75     @Override
76     @Nonnull
77     public ListenableFuture<Boolean> syncPolicy(@Nullable final Configuration dataAfter, @Nullable final Configuration dataBefore,
78                                                 final long version) {
79         ListenableFuture<Optional<Status>> creationResult;
80         if (dataBefore == null && dataAfter != null) {
81             creationResult = syncEndpoints(dataAfter, Create);
82         } else if (dataBefore != null && dataAfter == null) {
83             creationResult = syncEndpoints(dataBefore, Delete);
84         } else {
85             final ListenableFuture<Optional<Status>> deletionResult = syncEndpoints(dataBefore, Delete);
86             creationResult = Futures.transformAsync(deletionResult, new AsyncFunction<Optional<Status>, Optional<Status>>() {
87                 @Nonnull
88                 @Override
89                 public ListenableFuture<Optional<Status>> apply(@Nonnull Optional<Status> deletionResult) throws Exception {
90                     if (deletionResult.isPresent()) {
91                         // Wait till task is done. Result is not used, delete case has no status
92                         deletionResult.get();
93                     }
94                     return syncEndpoints(dataAfter, Create);
95                 }
96             });
97         }
98         return Futures.transformAsync(creationResult, new AsyncFunction<Optional<Status>, Boolean>() {
99             @Override
100             public ListenableFuture<Boolean> apply(@Nullable final Optional<Status> statusValue) throws Exception {
101                 Preconditions.checkArgument(statusValue != null, "provided status must not be null");
102                 return Futures.transform(reportPolicy(version, statusValue), new Function<Void, Boolean>() {
103                     @Override
104                     public Boolean apply(@Nullable final Void input) {
105                         return Boolean.TRUE;
106                     }
107                 });
108             }
109         });
110     }
111
112     /**
113      * Resolve policy for all endpoint pairs
114      *
115      * @param dataAfter - data used while processing
116      * @param action    - specifies whether data are intended for creating or removing of configuration
117      * @return status of policy resolution
118      */
119     @Nonnull
120     private ListenableFuture<Optional<Status>> syncEndpoints(final Configuration dataAfter, final DsAction action) {
121         if (dataAfter.getRendererEndpoints() == null
122                 || dataAfter.getRendererEndpoints().getRendererEndpoint() == null) {
123             LOG.debug("No configuration obtained - skipping");
124             return Futures.immediateFuture(Optional.empty());
125         }
126         final PolicyConfigurationContext context = new PolicyConfigurationContext();
127         // Renderer endpoint
128         for (RendererEndpoint rendererEndpoint : dataAfter.getRendererEndpoints().getRendererEndpoint()) {
129             context.setCurrentRendererEP(rendererEndpoint);
130
131             if (dataAfter.getEndpoints() == null || dataAfter.getEndpoints().getAddressEndpointWithLocation() == null) {
132                 final String info = "Renderer-endpoint: missing address-endpoint-with-location";
133                 context.appendUnconfiguredRendererEP(StatusUtil.assembleFullyNotConfigurableRendererEP(context, info));
134                 continue;
135             }
136             final List<AddressEndpointWithLocation> endpointsWithLocation = dataAfter.getEndpoints()
137                     .getAddressEndpointWithLocation();
138             final InstanceIdentifier mountpointIid = PolicyManagerUtil.getMountpointIidFromAbsoluteLocation(rendererEndpoint, endpointsWithLocation);
139             final DataBroker mountpoint = nodeManager.getNodeMountPoint(mountpointIid);
140             if (mountpoint == null) {
141                 final String info = String.format("No data-broker for mount-point [%s] available", mountpointIid);
142                 context.appendUnconfiguredRendererEP(StatusUtil.assembleFullyNotConfigurableRendererEP(context, info));
143                 continue;
144             }
145             final Optional<String> optionalManagementIpAddress = nodeManager.getNodeManagementIpByMountPointIid(mountpointIid);
146             if (! optionalManagementIpAddress.isPresent()) {
147                 final String info = String.format("Can not create policyWriter, managementIpAddress for mountpoint %s is null",
148                         mountpointIid);
149                 context.appendUnconfiguredRendererEP(StatusUtil.assembleFullyNotConfigurableRendererEP(context, info));
150                 continue;
151             }
152             final String managementIpAddress = optionalManagementIpAddress.get();
153             final String interfaceName = PolicyManagerUtil.getInterfaceNameFromAbsoluteLocation(rendererEndpoint, endpointsWithLocation);
154             final NodeId nodeId = nodeManager.getNodeIdByMountpointIid(mountpointIid);
155             if (interfaceName == null || nodeId == null) {
156                 final String info = String.format("Cannot compose policy-map, missing value. Interface: %s, NodeId: %s", interfaceName, nodeId);
157                 context.appendUnconfiguredRendererEP(StatusUtil.assembleFullyNotConfigurableRendererEP(context, info));
158                 LOG.warn(info);
159                 continue;
160             }
161             final String policyMapName = BASE_POLICY_MAP_NAME.concat(interfaceName);
162             final PolicyMapLocation policyMapLocation = new PolicyMapLocation(policyMapName, interfaceName, nodeId,
163                     managementIpAddress, mountpoint);
164             context.setPolicyMapLocation(policyMapLocation);
165
166             // TODO: pull timeout for async ops from config
167             final long TIMEOUT = 10;
168             final TimeUnit UNIT = TimeUnit.SECONDS;
169
170             final SendIpSgtBindingToPeerInputBuilder ipSgtBindings = new SendIpSgtBindingToPeerInputBuilder();
171             ipSgtBindings.setBinding(new ArrayList<>());
172
173             final Sgt sourceSgt = PolicyManagerUtil.findSgtTag(epToSgtMapper, rendererEndpoint, dataAfter.getEndpoints()
174                     .getAddressEndpointWithLocation(), TIMEOUT, UNIT);
175             final AddressEndpointWithLocation sourceEPAddressWithLocation = RendererPolicyUtil.lookupEndpoint(
176                     rendererEndpoint, dataAfter.getEndpoints().getAddressEndpointWithLocation());
177
178             PolicyManagerUtil.createIpSgtBindingItem(sourceSgt, sourceEPAddressWithLocation).ifPresent(ipSgtBindings.getBinding()::add);
179
180             // Peer Endpoint
181             for (PeerEndpoint peerEndpoint : rendererEndpoint.getPeerEndpoint()) {
182                 final Sgt destinationSgt = PolicyManagerUtil.findSgtTag(epToSgtMapper, peerEndpoint, dataAfter.getEndpoints()
183                         .getAddressEndpointWithLocation(), TIMEOUT, UNIT);
184                 final AddressEndpointWithLocation destinationEPAddressWithLocation = RendererPolicyUtil.lookupEndpoint(
185                         peerEndpoint, dataAfter.getEndpoints().getAddressEndpointWithLocation());
186                 PolicyManagerUtil.createIpSgtBindingItem(destinationSgt, destinationEPAddressWithLocation)
187                         .ifPresent(ipSgtBindings.getBinding()::add);
188
189                 if (sourceSgt == null || destinationSgt == null) {
190                     final String info = String.format("Endpoint-policy: missing sgt value(sourceSgt=%s, destinationSgt=%s)",
191                             sourceSgt, destinationSgt);
192                     context.appendUnconfiguredRendererEP(
193                             StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
194                     continue;
195                 }
196                 // Resolve policy between endpoints
197                 if (action.equals(Create)) {
198                     LOG.debug("Setting up policy between endpoint {}, sgt: {} and peer {}, sgt: {}", rendererEndpoint,
199                             sourceSgt, peerEndpoint, destinationSgt);
200                     PolicyManagerUtil.syncEndpointPairCreatePolicy(sourceSgt, destinationSgt, context, dataAfter,
201                             peerEndpoint, dataBroker);
202                 } else {
203                     LOG.debug("Removing policy between endpoint {}, sgt: {} and peer {}, sgt: {}", rendererEndpoint,
204                             sourceSgt, peerEndpoint, destinationSgt);
205                     PolicyManagerUtil.syncEndpointPairRemovePolicy(sourceSgt, destinationSgt, context, dataAfter,
206                             peerEndpoint);
207                 }
208             }
209
210             ipSgtDistributor.sendIpSgtBindingToPeer(ipSgtBindings.build());
211         }
212         final ListenableFuture<List<Boolean>> cumulativeResult = context.getCumulativeResult();
213         return Futures.transform(cumulativeResult, new Function<List<Boolean>, Optional<Status>>() {
214             @Nullable
215             @Override
216             public Optional<Status> apply(@Nullable final List<Boolean> input) {
217                 //TODO: inspect if all booleans are true
218
219                 LOG.trace("considering all submits as successful - otherwise there will be exception");
220                 final Status status = new StatusBuilder()
221                         .setUnconfiguredEndpoints(new UnconfiguredEndpointsBuilder()
222                                 .setUnconfiguredRendererEndpoint(context.getUnconfiguredRendererEPBag())
223                                 .build())
224                         .build();
225
226                 return Optional.of(status);
227             }
228         });
229     }
230
231     private CheckedFuture<Void, TransactionCommitFailedException> reportPolicy(final long version,
232                                                                                @Nonnull final Optional<Status> statusValue) {
233         if (statusValue.isPresent()) {
234             LOG.warn("IOS-XE renderer: operation not successfully completed, check unconfigured policy in operational/renderer:renderers");
235         }
236         final ReadWriteTransaction readWriteTransaction = dataBroker.newReadWriteTransaction();
237         final InstanceIdentifier<RendererPolicy> iid = InstanceIdentifier.create(Renderers.class)
238                 .child(Renderer.class, new RendererKey(IOS_XE_RENDERER))
239                 .child(RendererPolicy.class);
240         final RendererPolicy rendererPolicy = new RendererPolicyBuilder()
241                 .setVersion(version)
242                 .setStatus(statusValue.orElse(null))
243                 .build();
244         readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, iid, rendererPolicy);
245         return readWriteTransaction.submit();
246     }
247
248     @Override
249     public void close() {
250         //NOOP
251     }
252
253     enum DsAction {Create, Delete}
254
255     public enum ActionCase {ALLOW, CHAIN}
256
257     /**
258      * Wrapper class - contains all necessary information to clearly localize policy-map/interface/node in network
259      */
260     public static class PolicyMapLocation {
261
262         private final String policyMapName;
263         private final String interfaceName;
264         private final NodeId nodeId;
265         private final String managementIpAddress;
266         private final DataBroker mountpoint;
267
268         public PolicyMapLocation(final String policyMapName, final String interfaceName, final NodeId nodeId,
269                                  final String managementIpAddress, final DataBroker mountpoint) {
270             this.policyMapName = Preconditions.checkNotNull(policyMapName);
271             this.interfaceName = Preconditions.checkNotNull(interfaceName);
272             this.nodeId = Preconditions.checkNotNull(nodeId);
273             this.managementIpAddress = Preconditions.checkNotNull(managementIpAddress);
274             this.mountpoint = Preconditions.checkNotNull(mountpoint);
275         }
276
277         public String getPolicyMapName() {
278             return policyMapName;
279         }
280
281         public String getInterfaceName() {
282             return interfaceName;
283         }
284
285         public NodeId getNodeId() {
286             return nodeId;
287         }
288
289         public String getManagementIpAddress() {
290             return managementIpAddress;
291         }
292
293         public DataBroker getMountpoint() {
294             return mountpoint;
295         }
296     }
297 }