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