Merge "1. Corrected Ip Protocol match field. 2. Added mask for Ipv6 Extension header...
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / osgi / NetconfOperationRouterImpl.java
1 /*
2  * Copyright (c) 2013 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 package org.opendaylight.controller.netconf.impl.osgi;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Maps;
12 import com.google.common.collect.Sets;
13 import org.opendaylight.controller.netconf.api.NetconfSession;
14 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
15 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
16 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
17 import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
18 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
19 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
20 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
21 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStartExi;
22 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStopExi;
23 import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
24 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
25 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
26 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
27 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
28 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
29 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.w3c.dom.Document;
33
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.LinkedList;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42
43 public class NetconfOperationRouterImpl implements NetconfOperationRouter {
44
45     private static final Logger logger = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
46
47     private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
48
49     private final Set<NetconfOperation> allNetconfOperations;
50     private final TreeSet<NetconfOperationFilter> allSortedFilters;
51
52     private final CapabilityProvider capabilityProvider;
53
54
55     public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
56             CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
57
58         this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
59
60         this.capabilityProvider = capabilityProvider;
61
62         Set<NetconfOperation> defaultNetconfOperations = Sets.newHashSet();
63         defaultNetconfOperations.add(new DefaultGetSchema(capabilityProvider, netconfOperationServiceSnapshot
64                 .getNetconfSessionIdForReporting()));
65         defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot
66                 .getNetconfSessionIdForReporting()));
67         defaultNetconfOperations.add(new DefaultStartExi(netconfOperationServiceSnapshot
68                 .getNetconfSessionIdForReporting()));
69         defaultNetconfOperations.add(new DefaultStopExi(netconfOperationServiceSnapshot
70                 .getNetconfSessionIdForReporting()));
71
72         allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
73
74         DefaultCommit defaultCommit = new DefaultCommit(commitNotifier, capabilityProvider,
75                 netconfOperationServiceSnapshot.getNetconfSessionIdForReporting());
76         Set<NetconfOperationFilter> defaultFilters = Sets.<NetconfOperationFilter> newHashSet(defaultCommit);
77         allSortedFilters = getAllNetconfFilters(defaultFilters, netconfOperationServiceSnapshot);
78     }
79
80     private static Set<NetconfOperation> getAllNetconfOperations(Set<NetconfOperation> defaultNetconfOperations,
81             NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
82         Set<NetconfOperation> result = new HashSet<>();
83         result.addAll(defaultNetconfOperations);
84
85         for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
86             final Set<NetconfOperation> netOpsFromService = netconfOperationService.getNetconfOperations();
87             for (NetconfOperation netconfOperation : netOpsFromService) {
88                 Preconditions.checkState(result.contains(netconfOperation) == false,
89                         "Netconf operation %s already present", netconfOperation);
90                 result.add(netconfOperation);
91             }
92         }
93         return Collections.unmodifiableSet(result);
94     }
95
96     private static TreeSet<NetconfOperationFilter> getAllNetconfFilters(Set<NetconfOperationFilter> defaultFilters,
97             NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
98         TreeSet<NetconfOperationFilter> result = new TreeSet<>(defaultFilters);
99         for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
100             final Set<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
101             for (NetconfOperationFilter filter : filtersFromService) {
102                 Preconditions.checkState(result.contains(filter) == false,
103                         "Filter %s already present, all filters so far: %s", filter, result);
104                 result.add(filter);
105             }
106         }
107         return result;
108     }
109
110     public CapabilityProvider getCapabilityProvider() {
111         return capabilityProvider;
112     }
113
114     @Override
115     public synchronized Document onNetconfMessage(Document message,
116             NetconfSession session) throws NetconfDocumentedException {
117         NetconfOperationExecution netconfOperationExecution;
118
119         String messageAsString = XmlUtil.toString(message);
120
121         try {
122             netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
123         } catch (IllegalArgumentException | IllegalStateException e) {
124             logger.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
125
126             String errorMessage = String.format("Unable to handle rpc %s on session %s", messageAsString, session);
127             Map<String, String> errorInfo = Maps.newHashMap();
128
129             NetconfDocumentedException.ErrorTag tag = null;
130             if (e instanceof IllegalArgumentException) {
131                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_not_supported.toString(), e.getMessage());
132                 tag = NetconfDocumentedException.ErrorTag.operation_not_supported;
133             } else if (e instanceof IllegalStateException) {
134                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage());
135                 tag = NetconfDocumentedException.ErrorTag.operation_failed;
136             }
137
138             throw new NetconfDocumentedException(errorMessage, e, NetconfDocumentedException.ErrorType.application,
139                     tag, NetconfDocumentedException.ErrorSeverity.error, errorInfo);
140         } catch (RuntimeException e) {
141             throw handleUnexpectedEx("Unexpected exception during netconf operation sort", e);
142         }
143
144         try {
145             return executeOperationWithHighestPriority(message, netconfOperationExecution, messageAsString);
146         } catch (RuntimeException e) {
147             throw handleUnexpectedEx("Unexpected exception during netconf operation execution", e);
148         }
149     }
150
151     private NetconfDocumentedException handleUnexpectedEx(String s, Exception e) throws NetconfDocumentedException {
152         logger.error(s, e);
153
154         Map<String, String> info = Maps.newHashMap();
155         info.put(NetconfDocumentedException.ErrorSeverity.error.toString(), e.toString());
156         return new NetconfDocumentedException("Unexpected error",
157                 NetconfDocumentedException.ErrorType.application,
158                 NetconfDocumentedException.ErrorTag.operation_failed,
159                 NetconfDocumentedException.ErrorSeverity.error, info);
160     }
161
162     private Document executeOperationWithHighestPriority(Document message, NetconfOperationExecution netconfOperationExecution, String messageAsString) throws NetconfDocumentedException {
163         logger.debug("Forwarding netconf message {} to {}", messageAsString,
164                 netconfOperationExecution.operationWithHighestPriority);
165
166         final LinkedList<NetconfOperationFilterChain> chain = new LinkedList<>();
167         chain.push(netconfOperationExecution);
168
169         for (Iterator<NetconfOperationFilter> it = allSortedFilters.descendingIterator(); it.hasNext();) {
170             final NetconfOperationFilter filter = it.next();
171             final NetconfOperationFilterChain prevItem = chain.getFirst();
172             NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() {
173                 @Override
174                 public Document execute(Document message, NetconfOperationRouter operationRouter)
175                         throws NetconfDocumentedException {
176                     logger.trace("Entering {}", filter);
177                     return filter.doFilter(message, operationRouter, prevItem);
178                 }
179             };
180             chain.push(currentItem);
181         }
182         return chain.getFirst().execute(message, this);
183     }
184
185     private NetconfOperationExecution getNetconfOperationWithHighestPriority(
186             Document message, NetconfSession session) {
187
188         // TODO test
189         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = getSortedNetconfOperationsWithCanHandle(
190                 message, session);
191
192         Preconditions.checkArgument(sortedPriority.isEmpty() == false, "No %s available to handle message %s",
193                 NetconfOperation.class.getName(), XmlUtil.toString(message));
194
195         HandlingPriority highestFoundPriority = sortedPriority.lastKey();
196
197         int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size();
198
199         Preconditions.checkState(netconfOperationsWithHighestPriority == 1,
200                 "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message);
201
202         return new NetconfOperationExecution(sortedPriority, highestFoundPriority);
203     }
204
205     private TreeMap<HandlingPriority, Set<NetconfOperation>> getSortedNetconfOperationsWithCanHandle(
206             Document message, NetconfSession session) {
207         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = Maps.newTreeMap();
208
209         for (NetconfOperation netconfOperation : allNetconfOperations) {
210             final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
211             if (netconfOperation instanceof DefaultNetconfOperation) {
212                 ((DefaultNetconfOperation) netconfOperation)
213                         .setNetconfSession(session);
214             }
215             if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) {
216                 Set<NetconfOperation> netconfOperations = sortedPriority.get(handlingPriority);
217                 netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations);
218                 netconfOperations.add(netconfOperation);
219             }
220         }
221         return sortedPriority;
222     }
223
224     private Set<NetconfOperation> checkIfNoOperationsOnPriority(
225             TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority, HandlingPriority handlingPriority,
226             Set<NetconfOperation> netconfOperations) {
227         if (netconfOperations == null) {
228             netconfOperations = Sets.newHashSet();
229             sortedPriority.put(handlingPriority, netconfOperations);
230         }
231         return netconfOperations;
232     }
233
234     @Override
235     public void close() {
236         netconfOperationServiceSnapshot.close();
237     }
238
239     private class NetconfOperationExecution implements NetconfOperationFilterChain {
240         private final NetconfOperation operationWithHighestPriority;
241
242         private NetconfOperationExecution(NetconfOperation operationWithHighestPriority) {
243             this.operationWithHighestPriority = operationWithHighestPriority;
244         }
245
246         public NetconfOperationExecution(TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority,
247                 HandlingPriority highestFoundPriority) {
248             operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next();
249             sortedPriority.remove(highestFoundPriority);
250         }
251
252         @Override
253         public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException {
254             return operationWithHighestPriority.handle(message, router);
255         }
256     }
257
258     @Override
259     public String toString() {
260         return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
261                 + '}';
262     }
263
264
265
266 }