BUG-2635 Netconf monitoring for md-sal netconf northbound
[netconf.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.ImmutableSet;
12 import com.google.common.collect.Maps;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.NavigableMap;
17 import java.util.Set;
18 import java.util.TreeMap;
19 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
20 import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
21 import org.opendaylight.controller.netconf.impl.CommitNotifier;
22 import org.opendaylight.controller.netconf.impl.NetconfServerSession;
23 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
24 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
25 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultNetconfOperation;
26 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStartExi;
27 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStopExi;
28 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
29 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
30 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
31 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
32 import org.opendaylight.controller.netconf.mapping.api.SessionAwareNetconfOperation;
33 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.w3c.dom.Document;
37
38 public class NetconfOperationRouterImpl implements NetconfOperationRouter {
39
40     private static final Logger LOG = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
41     private final NetconfOperationService netconfOperationServiceSnapshot;
42     private final Collection<NetconfOperation> allNetconfOperations;
43
44     public NetconfOperationRouterImpl(final NetconfOperationService netconfOperationServiceSnapshot,
45                                       final CommitNotifier commitNotifier, final NetconfMonitoringService netconfMonitoringService, final String sessionId) {
46         this.netconfOperationServiceSnapshot = Preconditions.checkNotNull(netconfOperationServiceSnapshot);
47
48         final Set<NetconfOperation> ops = new HashSet<>();
49         ops.add(new DefaultCloseSession(sessionId, this));
50         ops.add(new DefaultStartExi(sessionId));
51         ops.add(new DefaultStopExi(sessionId));
52         ops.add(new DefaultCommit(commitNotifier, netconfMonitoringService, sessionId, this));
53
54         ops.addAll(netconfOperationServiceSnapshot.getNetconfOperations());
55
56         allNetconfOperations = ImmutableSet.copyOf(ops);
57     }
58
59     @Override
60     public Document onNetconfMessage(final Document message, final NetconfServerSession session) throws NetconfDocumentedException {
61         Preconditions.checkNotNull(allNetconfOperations, "Operation router was not initialized properly");
62
63         final NetconfOperationExecution netconfOperationExecution;
64         try {
65             netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
66         } catch (IllegalArgumentException | IllegalStateException e) {
67             final String messageAsString = XmlUtil.toString(message);
68             LOG.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
69
70             final NetconfDocumentedException.ErrorTag tag;
71             if (e instanceof IllegalArgumentException) {
72                 tag = NetconfDocumentedException.ErrorTag.operation_not_supported;
73             } else {
74                 tag = NetconfDocumentedException.ErrorTag.operation_failed;
75             }
76
77             throw new NetconfDocumentedException(
78                 String.format("Unable to handle rpc %s on session %s", messageAsString, session),
79                 e, NetconfDocumentedException.ErrorType.application,
80                 tag, NetconfDocumentedException.ErrorSeverity.error,
81                 Collections.singletonMap(tag.toString(), e.getMessage()));
82         } catch (RuntimeException e) {
83             throw handleUnexpectedEx("Unexpected exception during netconf operation sort", e);
84         }
85
86         try {
87             return executeOperationWithHighestPriority(message, netconfOperationExecution);
88         } catch (RuntimeException e) {
89             throw handleUnexpectedEx("Unexpected exception during netconf operation execution", e);
90         }
91     }
92
93     @Override
94     public void close() throws Exception {
95         netconfOperationServiceSnapshot.close();
96     }
97
98     private static NetconfDocumentedException handleUnexpectedEx(final String s, final Exception e) throws NetconfDocumentedException {
99         LOG.error("{}", s, e);
100         return new NetconfDocumentedException("Unexpected error",
101                 NetconfDocumentedException.ErrorType.application,
102                 NetconfDocumentedException.ErrorTag.operation_failed,
103                 NetconfDocumentedException.ErrorSeverity.error,
104                 Collections.singletonMap(NetconfDocumentedException.ErrorSeverity.error.toString(), e.toString()));
105     }
106
107     private Document executeOperationWithHighestPriority(final Document message,
108             final NetconfOperationExecution netconfOperationExecution)
109             throws NetconfDocumentedException {
110         if (LOG.isDebugEnabled()) {
111             LOG.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message), netconfOperationExecution.netconfOperation);
112         }
113
114         return netconfOperationExecution.execute(message);
115     }
116
117     private NetconfOperationExecution getNetconfOperationWithHighestPriority(
118             final Document message, final NetconfServerSession session) throws NetconfDocumentedException {
119
120         NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority = getSortedNetconfOperationsWithCanHandle(
121                 message, session);
122
123         if (sortedByPriority.isEmpty()) {
124             throw new IllegalArgumentException(String.format("No %s available to handle message %s",
125                 NetconfOperation.class.getName(), XmlUtil.toString(message)));
126         }
127
128         return NetconfOperationExecution.createExecutionChain(sortedByPriority, sortedByPriority.lastKey());
129     }
130
131     private TreeMap<HandlingPriority, NetconfOperation> getSortedNetconfOperationsWithCanHandle(final Document message,
132             final NetconfServerSession session) throws NetconfDocumentedException {
133         TreeMap<HandlingPriority, NetconfOperation> sortedPriority = Maps.newTreeMap();
134
135         for (NetconfOperation netconfOperation : allNetconfOperations) {
136             final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
137             if (netconfOperation instanceof DefaultNetconfOperation) {
138                 ((DefaultNetconfOperation) netconfOperation).setNetconfSession(session);
139             }
140             if(netconfOperation instanceof SessionAwareNetconfOperation) {
141                 ((SessionAwareNetconfOperation) netconfOperation).setSession(session);
142             }
143             if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
144
145                 Preconditions.checkState(!sortedPriority.containsKey(handlingPriority),
146                         "Multiple %s available to handle message %s with priority %s, %s and %s",
147                         NetconfOperation.class.getName(), message, handlingPriority, netconfOperation, sortedPriority.get(handlingPriority));
148                 sortedPriority.put(handlingPriority, netconfOperation);
149             }
150         }
151         return sortedPriority;
152     }
153
154     public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
155         @Override
156         public boolean isExecutionTermination() {
157             return true;
158         }
159
160         @Override
161         public Document execute(final Document requestMessage) throws NetconfDocumentedException {
162             throw new NetconfDocumentedException("This execution represents the termination point in operation execution and cannot be executed itself",
163                     NetconfDocumentedException.ErrorType.application,
164                     NetconfDocumentedException.ErrorTag.operation_failed,
165                     NetconfDocumentedException.ErrorSeverity.error);
166         }
167     };
168
169     private static class NetconfOperationExecution implements NetconfOperationChainedExecution {
170         private final NetconfOperation netconfOperation;
171         private final NetconfOperationChainedExecution subsequentExecution;
172
173         private NetconfOperationExecution(final NetconfOperation netconfOperation, final NetconfOperationChainedExecution subsequentExecution) {
174             this.netconfOperation = netconfOperation;
175             this.subsequentExecution = subsequentExecution;
176         }
177
178         @Override
179         public boolean isExecutionTermination() {
180             return false;
181         }
182
183         @Override
184         public Document execute(final Document message) throws NetconfDocumentedException {
185             return netconfOperation.handle(message, subsequentExecution);
186         }
187
188         public static NetconfOperationExecution createExecutionChain(
189                 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority, final HandlingPriority handlingPriority) {
190             NetconfOperation netconfOperation = sortedByPriority.get(handlingPriority);
191             HandlingPriority subsequentHandlingPriority = sortedByPriority.lowerKey(handlingPriority);
192
193             NetconfOperationChainedExecution subsequentExecution = null;
194
195             if (subsequentHandlingPriority != null) {
196                 subsequentExecution = createExecutionChain(sortedByPriority, subsequentHandlingPriority);
197             } else {
198                 subsequentExecution = EXECUTION_TERMINATION_POINT;
199             }
200
201             return new NetconfOperationExecution(netconfOperation, subsequentExecution);
202         }
203     }
204
205     @Override
206     public String toString() {
207         return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
208                 + '}';
209     }
210 }