Merge changes I50218f21,I6bc2631b
[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(
125                     message, session);
126         } catch (IllegalArgumentException | IllegalStateException e) {
127             logger.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
128
129             String errorMessage = String.format("Unable to handle rpc %s on session %s", messageAsString, session);
130             Map<String, String> errorInfo = Maps.newHashMap();
131
132             NetconfDocumentedException.ErrorTag tag = null;
133             if (e instanceof IllegalArgumentException) {
134                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_not_supported.toString(), e.getMessage());
135                 tag = NetconfDocumentedException.ErrorTag.operation_not_supported;
136             } else if (e instanceof IllegalStateException) {
137                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage());
138                 tag = NetconfDocumentedException.ErrorTag.operation_failed;
139             }
140
141             throw new NetconfDocumentedException(errorMessage, e, NetconfDocumentedException.ErrorType.application,
142                     tag, NetconfDocumentedException.ErrorSeverity.error, errorInfo);
143         }
144
145         logger.debug("Forwarding netconf message {} to {}", messageAsString,
146                 netconfOperationExecution.operationWithHighestPriority);
147
148         final LinkedList<NetconfOperationFilterChain> chain = new LinkedList<>();
149         chain.push(netconfOperationExecution);
150
151         for (Iterator<NetconfOperationFilter> it = allSortedFilters.descendingIterator(); it.hasNext();) {
152             final NetconfOperationFilter filter = it.next();
153             final NetconfOperationFilterChain prevItem = chain.getFirst();
154             NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() {
155                 @Override
156                 public Document execute(Document message, NetconfOperationRouter operationRouter)
157                         throws NetconfDocumentedException {
158                     logger.trace("Entering {}", filter);
159                     return filter.doFilter(message, operationRouter, prevItem);
160                 }
161             };
162             chain.push(currentItem);
163         }
164         return chain.getFirst().execute(message, this);
165     }
166
167     private NetconfOperationExecution getNetconfOperationWithHighestPriority(
168             Document message, NetconfSession session) {
169
170         // TODO test
171         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = getSortedNetconfOperationsWithCanHandle(
172                 message, session);
173
174         Preconditions.checkArgument(sortedPriority.isEmpty() == false, "No %s available to handle message %s",
175                 NetconfOperation.class.getName(), XmlUtil.toString(message));
176
177         HandlingPriority highestFoundPriority = sortedPriority.lastKey();
178
179         int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size();
180
181         Preconditions.checkState(netconfOperationsWithHighestPriority == 1,
182                 "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message);
183
184         return new NetconfOperationExecution(sortedPriority, highestFoundPriority);
185     }
186
187     private TreeMap<HandlingPriority, Set<NetconfOperation>> getSortedNetconfOperationsWithCanHandle(
188             Document message, NetconfSession session) {
189         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = Maps.newTreeMap();
190
191         for (NetconfOperation netconfOperation : allNetconfOperations) {
192             final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
193             if (netconfOperation instanceof DefaultNetconfOperation) {
194                 ((DefaultNetconfOperation) netconfOperation)
195                         .setNetconfSession(session);
196             }
197             if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) {
198                 Set<NetconfOperation> netconfOperations = sortedPriority.get(handlingPriority);
199                 netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations);
200                 netconfOperations.add(netconfOperation);
201             }
202         }
203         return sortedPriority;
204     }
205
206     private Set<NetconfOperation> checkIfNoOperationsOnPriority(
207             TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority, HandlingPriority handlingPriority,
208             Set<NetconfOperation> netconfOperations) {
209         if (netconfOperations == null) {
210             netconfOperations = Sets.newHashSet();
211             sortedPriority.put(handlingPriority, netconfOperations);
212         }
213         return netconfOperations;
214     }
215
216     @Override
217     public void close() {
218         netconfOperationServiceSnapshot.close();
219     }
220
221     private class NetconfOperationExecution implements NetconfOperationFilterChain {
222         private final NetconfOperation operationWithHighestPriority;
223
224         private NetconfOperationExecution(NetconfOperation operationWithHighestPriority) {
225             this.operationWithHighestPriority = operationWithHighestPriority;
226         }
227
228         public NetconfOperationExecution(TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority,
229                 HandlingPriority highestFoundPriority) {
230             operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next();
231             sortedPriority.remove(highestFoundPriority);
232         }
233
234         @Override
235         public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException {
236             return operationWithHighestPriority.handle(message, router);
237         }
238     }
239
240     @Override
241     public String toString() {
242         return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
243                 + '}';
244     }
245
246
247
248 }