blob: c0fcce39bd39fbf310a229f52926ea4a3788494c [file] [log] [blame]
andreaeb70a942015-10-16 21:34:46 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
andreaeb70a942015-10-16 21:34:46 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Yuta HIGUCHIe3ae8212017-04-20 10:18:41 -070017package org.onosproject.netconf.ctl.impl;
andreaeb70a942015-10-16 21:34:46 -070018
Akihiro Yamanouchi45122222016-07-15 13:13:11 +090019import com.google.common.annotations.Beta;
andreaeb70a942015-10-16 21:34:46 -070020import ch.ethz.ssh2.Connection;
21import ch.ethz.ssh2.Session;
Andrea Campanellac3627842017-04-04 18:06:54 +020022import ch.ethz.ssh2.channel.Channel;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -070023
24import com.google.common.base.MoreObjects;
25import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070026import com.google.common.base.Preconditions;
Andrei Mihaescuac542ca2017-03-26 21:36:25 +030027import org.onosproject.netconf.TargetConfig;
Yuta HIGUCHIe3ae8212017-04-20 10:18:41 -070028import org.onosproject.netconf.FilteringNetconfDeviceOutputEventListener;
andreaeb70a942015-10-16 21:34:46 -070029import org.onosproject.netconf.NetconfDeviceInfo;
Andrea Campanella101417d2015-12-11 17:58:07 -080030import org.onosproject.netconf.NetconfDeviceOutputEvent;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -070031import org.onosproject.netconf.NetconfDeviceOutputEvent.Type;
Andrea Campanella101417d2015-12-11 17:58:07 -080032import org.onosproject.netconf.NetconfDeviceOutputEventListener;
33import org.onosproject.netconf.NetconfException;
andreaeb70a942015-10-16 21:34:46 -070034import org.onosproject.netconf.NetconfSession;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
andreaeb70a942015-10-16 21:34:46 -070038import java.io.IOException;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020039import java.util.ArrayList;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -070040import java.util.Collection;
Andrea Campanella1cd641b2015-12-07 17:28:34 -080041import java.util.Collections;
Aaron Kruglikov72db6422017-02-13 12:16:51 -080042import java.util.LinkedHashSet;
andreaeb70a942015-10-16 21:34:46 -070043import java.util.List;
Andrea Campanella101417d2015-12-11 17:58:07 -080044import java.util.Map;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020045import java.util.Optional;
Aaron Kruglikov72db6422017-02-13 12:16:51 -080046import java.util.Set;
Andrea Campanella101417d2015-12-11 17:58:07 -080047import java.util.concurrent.CompletableFuture;
Sean Condond2c8d472017-02-17 17:09:39 +000048import java.util.concurrent.ConcurrentHashMap;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -070049import java.util.concurrent.CopyOnWriteArrayList;
Andrea Campanellab029b9e2016-01-29 11:05:36 -080050import java.util.concurrent.ExecutionException;
51import java.util.concurrent.TimeUnit;
52import java.util.concurrent.TimeoutException;
Andrea Campanella101417d2015-12-11 17:58:07 -080053import java.util.concurrent.atomic.AtomicInteger;
Sean Condond2c8d472017-02-17 17:09:39 +000054
Aaron Kruglikov72db6422017-02-13 12:16:51 -080055import java.util.regex.Pattern;
56import java.util.regex.Matcher;
Andrea Campanella101417d2015-12-11 17:58:07 -080057
andreaeb70a942015-10-16 21:34:46 -070058/**
59 * Implementation of a NETCONF session to talk to a device.
60 */
61public class NetconfSessionImpl implements NetconfSession {
62
Andrea Campanella101417d2015-12-11 17:58:07 -080063 private static final Logger log = LoggerFactory
andreaeb70a942015-10-16 21:34:46 -070064 .getLogger(NetconfSessionImpl.class);
Andrea Campanella101417d2015-12-11 17:58:07 -080065
Andrea Campanella101417d2015-12-11 17:58:07 -080066 private static final String ENDPATTERN = "]]>]]>";
Andrea Campanella101417d2015-12-11 17:58:07 -080067 private static final String MESSAGE_ID_STRING = "message-id";
Andrea Campanella1311ea02016-03-04 17:51:25 -080068 private static final String HELLO = "<hello";
Andrea Campanella101417d2015-12-11 17:58:07 -080069 private static final String NEW_LINE = "\n";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080070 private static final String END_OF_RPC_OPEN_TAG = "\">";
71 private static final String EQUAL = "=";
72 private static final String NUMBER_BETWEEN_QUOTES_MATCHER = "\"+([0-9]+)+\"";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090073 private static final String RPC_OPEN = "<rpc ";
74 private static final String RPC_CLOSE = "</rpc>";
75 private static final String GET_OPEN = "<get>";
76 private static final String GET_CLOSE = "</get>";
77 private static final String WITH_DEFAULT_OPEN = "<with-defaults ";
78 private static final String WITH_DEFAULT_CLOSE = "</with-defaults>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090079 private static final String DEFAULT_OPERATION_OPEN = "<default-operation>";
80 private static final String DEFAULT_OPERATION_CLOSE = "</default-operation>";
Akihiro Yamanouchi45122222016-07-15 13:13:11 +090081 private static final String SUBTREE_FILTER_OPEN = "<filter type=\"subtree\">";
82 private static final String SUBTREE_FILTER_CLOSE = "</filter>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090083 private static final String EDIT_CONFIG_OPEN = "<edit-config>";
84 private static final String EDIT_CONFIG_CLOSE = "</edit-config>";
85 private static final String TARGET_OPEN = "<target>";
86 private static final String TARGET_CLOSE = "</target>";
Sean Condonb0720e72017-01-10 12:29:02 +000087 private static final String CONFIG_OPEN = "<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090088 private static final String CONFIG_CLOSE = "</config>";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080089 private static final String XML_HEADER =
90 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090091 private static final String NETCONF_BASE_NAMESPACE =
92 "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"";
93 private static final String NETCONF_WITH_DEFAULTS_NAMESPACE =
94 "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"";
Akihiro Yamanouchi45122222016-07-15 13:13:11 +090095 private static final String SUBSCRIPTION_SUBTREE_FILTER_OPEN =
96 "<filter xmlns:base10=\"urn:ietf:params:xml:ns:netconf:base:1.0\" base10:type=\"subtree\">";
andreaeb70a942015-10-16 21:34:46 -070097
Aaron Kruglikov72db6422017-02-13 12:16:51 -080098 private static final String INTERLEAVE_CAPABILITY_STRING = "urn:ietf:params:netconf:capability:interleave:1.0";
Sean Condond2c8d472017-02-17 17:09:39 +000099
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800100 private static final String CAPABILITY_REGEX = "<capability>\\s*(.*?)\\s*</capability>";
101 private static final Pattern CAPABILITY_REGEX_PATTERN = Pattern.compile(CAPABILITY_REGEX);
102
103 private static final String SESSION_ID_REGEX = "<session-id>\\s*(.*?)\\s*</session-id>";
104 private static final Pattern SESSION_ID_REGEX_PATTERN = Pattern.compile(SESSION_ID_REGEX);
105
106 private String sessionID;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800107 private final AtomicInteger messageIdInteger = new AtomicInteger(0);
andreaeb70a942015-10-16 21:34:46 -0700108 private Connection netconfConnection;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700109 protected final NetconfDeviceInfo deviceInfo;
andreaeb70a942015-10-16 21:34:46 -0700110 private Session sshSession;
111 private boolean connectionActive;
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800112 private Iterable<String> onosCapabilities =
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800113 Collections.singletonList("urn:ietf:params:netconf:base:1.0");
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800114
115 /* NOTE: the "serverHelloResponseOld" is deprecated in 1.10.0 and should eventually be removed */
116 @Deprecated
117 private String serverHelloResponseOld;
118 private final Set<String> deviceCapabilities = new LinkedHashSet<>();
helenyrwu0407c642016-06-09 12:01:30 -0700119 private NetconfStreamHandler streamHandler;
Andrea Campanella101417d2015-12-11 17:58:07 -0800120 private Map<Integer, CompletableFuture<String>> replies;
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200121 private List<String> errorReplies;
helenyrwu0407c642016-06-09 12:01:30 -0700122 private boolean subscriptionConnected = false;
Andrea Campanellac3627842017-04-04 18:06:54 +0200123 private String notificationFilterSchema = null;
andreaeb70a942015-10-16 21:34:46 -0700124
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700125 private final Collection<NetconfDeviceOutputEventListener> primaryListeners =
126 new CopyOnWriteArrayList<>();
127 private final Collection<NetconfSession> children =
128 new CopyOnWriteArrayList<>();
129
andreaeb70a942015-10-16 21:34:46 -0700130
Andrea Campanella101417d2015-12-11 17:58:07 -0800131 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700132 this.deviceInfo = deviceInfo;
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900133 this.netconfConnection = null;
134 this.sshSession = null;
andreaeb70a942015-10-16 21:34:46 -0700135 connectionActive = false;
Sean Condond2c8d472017-02-17 17:09:39 +0000136 replies = new ConcurrentHashMap<>();
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200137 errorReplies = new ArrayList<>();
andreaeb70a942015-10-16 21:34:46 -0700138 startConnection();
139 }
140
Andrea Campanella101417d2015-12-11 17:58:07 -0800141 private void startConnection() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700142 if (!connectionActive) {
143 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
Sean Condon334ad692016-12-13 17:56:56 +0000144 int connectTimeout = NetconfControllerImpl.netconfConnectTimeout;
145
Andrea Campanella101417d2015-12-11 17:58:07 -0800146 try {
Sean Condon334ad692016-12-13 17:56:56 +0000147 netconfConnection.connect(null, 1000 * connectTimeout, 1000 * connectTimeout);
Andrea Campanella101417d2015-12-11 17:58:07 -0800148 } catch (IOException e) {
Yuta HIGUCHI0184a7b2017-03-31 13:13:58 -0700149 throw new NetconfException("Cannot open a connection with device " + deviceInfo, e);
Andrea Campanella101417d2015-12-11 17:58:07 -0800150 }
andreaeb70a942015-10-16 21:34:46 -0700151 boolean isAuthenticated;
152 try {
Andrea Campanellae7006dc2017-02-15 16:04:09 -0800153 if (deviceInfo.getKeyFile() != null && deviceInfo.getKeyFile().canRead()) {
154 log.debug("Authenticating with key file to device {} with username {}",
155 deviceInfo.getDeviceId(), deviceInfo.name());
156 isAuthenticated = netconfConnection.authenticateWithPublicKey(
157 deviceInfo.name(), deviceInfo.getKeyFile(),
158 deviceInfo.password().equals("") ? null : deviceInfo.password());
159 } else if (deviceInfo.getKey() != null) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600160 log.debug("Authenticating with key to device {} with username {}",
Andrea Campanellac3627842017-04-04 18:06:54 +0200161 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700162 isAuthenticated = netconfConnection.authenticateWithPublicKey(
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600163 deviceInfo.name(), deviceInfo.getKey(),
164 deviceInfo.password().equals("") ? null : deviceInfo.password());
andreaeb70a942015-10-16 21:34:46 -0700165 } else {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600166 log.debug("Authenticating to device {} with username {} with password",
Andrea Campanella50d25212016-02-26 13:06:23 -0800167 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700168 isAuthenticated = netconfConnection.authenticateWithPassword(
169 deviceInfo.name(), deviceInfo.password());
170 }
171 } catch (IOException e) {
Ryan Goulding69524392016-12-03 13:21:20 -0500172 log.error("Authentication connection to device {} failed",
173 deviceInfo.getDeviceId(), e);
Andrea Campanella101417d2015-12-11 17:58:07 -0800174 throw new NetconfException("Authentication connection to device " +
175 deviceInfo.getDeviceId() + " failed", e);
andreaeb70a942015-10-16 21:34:46 -0700176 }
177
178 connectionActive = true;
179 Preconditions.checkArgument(isAuthenticated,
Andrea Campanellad264b492016-03-01 09:46:06 -0800180 "Authentication to device %s with username " +
181 "%s failed",
Andrea Campanella50d25212016-02-26 13:06:23 -0800182 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700183 startSshSession();
184 }
185 }
186
Andrea Campanella101417d2015-12-11 17:58:07 -0800187 private void startSshSession() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700188 try {
189 sshSession = netconfConnection.openSession();
190 sshSession.startSubSystem("netconf");
helenyrwu0407c642016-06-09 12:01:30 -0700191 streamHandler = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
192 sshSession.getStderr(), deviceInfo,
Sean Condond2c8d472017-02-17 17:09:39 +0000193 new NetconfSessionDelegateImpl(),
194 replies);
Yuta HIGUCHIe3ae8212017-04-20 10:18:41 -0700195 this.addDeviceOutputListener(new FilteringNetconfDeviceOutputEventListener(deviceInfo));
andreaeb70a942015-10-16 21:34:46 -0700196 sendHello();
197 } catch (IOException e) {
Andrea Campanella0cee0b62017-04-03 20:02:54 +0200198 log.error("Failed to create ch.ethz.ssh2.Session session {} ", e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800199 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
200 deviceInfo, e);
andreaeb70a942015-10-16 21:34:46 -0700201 }
202 }
203
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900204
205 @Beta
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700206 protected void startSubscriptionStream(String filterSchema) throws NetconfException {
207 boolean openNewSession = false;
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800208 if (!deviceCapabilities.contains(INTERLEAVE_CAPABILITY_STRING)) {
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700209 log.info("Device {} doesn't support interleave, creating child session", deviceInfo);
210 openNewSession = true;
211
212 } else if (subscriptionConnected &&
213 notificationFilterSchema != null &&
214 !Objects.equal(filterSchema, notificationFilterSchema)) {
215 // interleave supported and existing filter is NOT "no filtering"
216 // and was requested with different filtering schema
217 log.info("Cannot use existing session for subscription {} ({})",
218 deviceInfo, filterSchema);
219 openNewSession = true;
helenyrwu0407c642016-06-09 12:01:30 -0700220 }
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700221
222 if (openNewSession) {
223 log.info("Creating notification session to {} with filter {}",
224 deviceInfo, filterSchema);
225 NetconfSession child = new NotificationSession(deviceInfo);
226
227 child.addDeviceOutputListener(new NotificationForwarder());
228
229 child.startSubscription(filterSchema);
230 children.add(child);
231 return;
232 }
233
234 // request to start interleaved notification session
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900235 String reply = sendRequest(createSubscriptionString(filterSchema));
helenyrwu0407c642016-06-09 12:01:30 -0700236 if (!checkReply(reply)) {
237 throw new NetconfException("Subscription not successful with device "
238 + deviceInfo + " with reply " + reply);
239 }
240 subscriptionConnected = true;
241 }
242
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900243 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700244 public void startSubscription() throws NetconfException {
245 if (!subscriptionConnected) {
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700246 startSubscriptionStream(null);
helenyrwu0407c642016-06-09 12:01:30 -0700247 }
248 streamHandler.setEnableNotifications(true);
249 }
250
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900251 @Beta
252 @Override
253 public void startSubscription(String filterSchema) throws NetconfException {
254 if (!subscriptionConnected) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200255 notificationFilterSchema = filterSchema;
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700256 startSubscriptionStream(filterSchema);
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900257 }
258 streamHandler.setEnableNotifications(true);
259 }
260
261 @Beta
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700262 protected String createSubscriptionString(String filterSchema) {
helenyrwu0407c642016-06-09 12:01:30 -0700263 StringBuilder subscriptionbuffer = new StringBuilder();
264 subscriptionbuffer.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
265 subscriptionbuffer.append(" <create-subscription\n");
266 subscriptionbuffer.append("xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n");
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900267 // FIXME Only subtree filtering supported at the moment.
268 if (filterSchema != null) {
269 subscriptionbuffer.append(" ");
270 subscriptionbuffer.append(SUBSCRIPTION_SUBTREE_FILTER_OPEN).append(NEW_LINE);
271 subscriptionbuffer.append(filterSchema).append(NEW_LINE);
272 subscriptionbuffer.append(" ");
273 subscriptionbuffer.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
274 }
helenyrwu0407c642016-06-09 12:01:30 -0700275 subscriptionbuffer.append(" </create-subscription>\n");
276 subscriptionbuffer.append("</rpc>\n");
277 subscriptionbuffer.append(ENDPATTERN);
278 return subscriptionbuffer.toString();
279 }
280
281 @Override
282 public void endSubscription() throws NetconfException {
283 if (subscriptionConnected) {
284 streamHandler.setEnableNotifications(false);
285 } else {
286 throw new NetconfException("Subscription does not exist.");
287 }
288 }
289
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800290 private void sendHello() throws NetconfException {
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800291 serverHelloResponseOld = sendRequest(createHelloString());
292 Matcher capabilityMatcher = CAPABILITY_REGEX_PATTERN.matcher(serverHelloResponseOld);
293 while (capabilityMatcher.find()) {
294 deviceCapabilities.add(capabilityMatcher.group(1));
295 }
296 sessionID = String.valueOf(-1);
297 Matcher sessionIDMatcher = SESSION_ID_REGEX_PATTERN.matcher(serverHelloResponseOld);
298 if (sessionIDMatcher.find()) {
299 sessionID = sessionIDMatcher.group(1);
300 } else {
301 throw new NetconfException("Missing SessionID in server hello " +
302 "reponse.");
303 }
304
andreaeb70a942015-10-16 21:34:46 -0700305 }
306
307 private String createHelloString() {
308 StringBuilder hellobuffer = new StringBuilder();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800309 hellobuffer.append(XML_HEADER);
310 hellobuffer.append("\n");
andreaeb70a942015-10-16 21:34:46 -0700311 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
312 hellobuffer.append(" <capabilities>\n");
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800313 onosCapabilities.forEach(
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800314 cap -> hellobuffer.append(" <capability>")
315 .append(cap)
316 .append("</capability>\n"));
andreaeb70a942015-10-16 21:34:46 -0700317 hellobuffer.append(" </capabilities>\n");
318 hellobuffer.append("</hello>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800319 hellobuffer.append(ENDPATTERN);
andreaeb70a942015-10-16 21:34:46 -0700320 return hellobuffer.toString();
321
322 }
323
Yuta HIGUCHIe3ae8212017-04-20 10:18:41 -0700324 @Override
Andrea Campanellac3627842017-04-04 18:06:54 +0200325 public void checkAndReestablish() throws NetconfException {
326 if (sshSession.getState() != Channel.STATE_OPEN) {
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800327 try {
Andrea Campanellac3627842017-04-04 18:06:54 +0200328 log.debug("Trying to reopen the Sesion with {}", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800329 startSshSession();
Andrea Campanellac3627842017-04-04 18:06:54 +0200330 } catch (IOException | IllegalStateException e) {
331 log.debug("Trying to reopen the Connection with {}", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800332 try {
Andrea Campanella0cee0b62017-04-03 20:02:54 +0200333 connectionActive = false;
334 replies.clear();
335 messageIdInteger.set(0);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800336 startConnection();
Andrea Campanellac3627842017-04-04 18:06:54 +0200337 if (subscriptionConnected) {
338 log.debug("Restarting subscription with {}", deviceInfo.getDeviceId());
339 subscriptionConnected = false;
340 startSubscription(notificationFilterSchema);
341 }
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800342 } catch (IOException e2) {
Andrea Campanella0cee0b62017-04-03 20:02:54 +0200343 log.error("No connection {} for device {}", netconfConnection, e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800344 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800345 }
346 }
347 }
348 }
349
andreaeb70a942015-10-16 21:34:46 -0700350 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800351 public String requestSync(String request) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800352 if (!request.contains(ENDPATTERN)) {
353 request = request + NEW_LINE + ENDPATTERN;
354 }
355 String reply = sendRequest(request);
Andreas Papazois2e557be2016-06-04 15:39:56 +0300356 checkReply(reply);
357 return reply;
andreaeb70a942015-10-16 21:34:46 -0700358 }
359
360 @Override
Sean Condond2c8d472017-02-17 17:09:39 +0000361 @Deprecated
Andrea Campanella101417d2015-12-11 17:58:07 -0800362 public CompletableFuture<String> request(String request) {
Sean Condond2c8d472017-02-17 17:09:39 +0000363 return streamHandler.sendMessage(request);
364 }
365
366 private CompletableFuture<String> request(String request, int messageId) {
367 return streamHandler.sendMessage(request, messageId);
Andrea Campanella101417d2015-12-11 17:58:07 -0800368 }
369
370 private String sendRequest(String request) throws NetconfException {
Andrea Campanellac3627842017-04-04 18:06:54 +0200371 checkAndReestablish();
Sean Condond2c8d472017-02-17 17:09:39 +0000372 final int messageId = messageIdInteger.getAndIncrement();
373 request = formatRequestMessageId(request, messageId);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800374 request = formatXmlHeader(request);
Sean Condond2c8d472017-02-17 17:09:39 +0000375 CompletableFuture<String> futureReply = request(request, messageId);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300376 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800377 String rp;
378 try {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300379 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
Sean Condond2c8d472017-02-17 17:09:39 +0000380 replies.remove(messageId);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800381 } catch (InterruptedException | ExecutionException | TimeoutException e) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200382 throw new NetconfException("No matching reply for request " + request, e);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800383 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800384 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
Andrea Campanella50d25212016-02-26 13:06:23 -0800385 return rp.trim();
Andrea Campanella101417d2015-12-11 17:58:07 -0800386 }
387
Sean Condond2c8d472017-02-17 17:09:39 +0000388 private String formatRequestMessageId(String request, int messageId) {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800389 if (request.contains(MESSAGE_ID_STRING)) {
Sean Condond2c8d472017-02-17 17:09:39 +0000390 //FIXME if application provides his own counting of messages this fails that count
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800391 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
Sean Condond2c8d472017-02-17 17:09:39 +0000392 MESSAGE_ID_STRING + EQUAL + "\"" + messageId + "\"");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800393 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
394 //FIXME find out a better way to enforce the presence of message-id
395 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
Sean Condond2c8d472017-02-17 17:09:39 +0000396 + messageId + "\"" + ">");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800397 }
398 return request;
399 }
400
401 private String formatXmlHeader(String request) {
402 if (!request.contains(XML_HEADER)) {
403 //FIXME if application provieds his own XML header of different type there is a clash
404 request = XML_HEADER + "\n" + request;
405 }
406 return request;
407 }
408
Andrea Campanella101417d2015-12-11 17:58:07 -0800409 @Override
Akihiro Yamanouchi8d3a9d32016-07-12 11:41:44 +0900410 public String doWrappedRpc(String request) throws NetconfException {
411 StringBuilder rpc = new StringBuilder(XML_HEADER);
412 rpc.append(RPC_OPEN);
413 rpc.append(MESSAGE_ID_STRING);
414 rpc.append(EQUAL);
415 rpc.append("\"");
416 rpc.append(messageIdInteger.get());
417 rpc.append("\" ");
418 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
419 rpc.append(request);
420 rpc.append(RPC_CLOSE).append(NEW_LINE);
421 rpc.append(ENDPATTERN);
422 String reply = sendRequest(rpc.toString());
423 checkReply(reply);
424 return reply;
425 }
426
427 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800428 public String get(String request) throws NetconfException {
429 return requestSync(request);
430 }
431
432 @Override
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900433 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
434 StringBuilder rpc = new StringBuilder(XML_HEADER);
435 rpc.append(RPC_OPEN);
436 rpc.append(MESSAGE_ID_STRING);
437 rpc.append(EQUAL);
438 rpc.append("\"");
439 rpc.append(messageIdInteger.get());
440 rpc.append("\" ");
441 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
442 rpc.append(GET_OPEN).append(NEW_LINE);
443 if (filterSchema != null) {
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900444 rpc.append(SUBTREE_FILTER_OPEN).append(NEW_LINE);
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900445 rpc.append(filterSchema).append(NEW_LINE);
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900446 rpc.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900447 }
448 if (withDefaultsMode != null) {
449 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
450 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
451 }
452 rpc.append(GET_CLOSE).append(NEW_LINE);
453 rpc.append(RPC_CLOSE).append(NEW_LINE);
454 rpc.append(ENDPATTERN);
455 String reply = sendRequest(rpc.toString());
456 checkReply(reply);
457 return reply;
458 }
459
460 @Override
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300461 public String getConfig(TargetConfig netconfTargetConfig) throws NetconfException {
462 return getConfig(netconfTargetConfig, null);
andreaeb70a942015-10-16 21:34:46 -0700463 }
464
465 @Override
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300466 public String getConfig(String netconfTargetConfig) throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700467 return getConfig(TargetConfig.toTargetConfig(netconfTargetConfig));
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300468 }
469
470 @Override
471 public String getConfig(String netconfTargetConfig, String configurationFilterSchema) throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700472 return getConfig(TargetConfig.toTargetConfig(netconfTargetConfig), configurationFilterSchema);
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300473 }
474
475 @Override
476 public String getConfig(TargetConfig netconfTargetConfig, String configurationSchema) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800477 StringBuilder rpc = new StringBuilder(XML_HEADER);
478 rpc.append("<rpc ");
479 rpc.append(MESSAGE_ID_STRING);
480 rpc.append(EQUAL);
481 rpc.append("\"");
482 rpc.append(messageIdInteger.get());
483 rpc.append("\" ");
484 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700485 rpc.append("<get-config>\n");
486 rpc.append("<source>\n");
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300487 rpc.append("<").append(netconfTargetConfig).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700488 rpc.append("</source>");
489 if (configurationSchema != null) {
490 rpc.append("<filter type=\"subtree\">\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800491 rpc.append(configurationSchema).append("\n");
andreaeb70a942015-10-16 21:34:46 -0700492 rpc.append("</filter>\n");
493 }
494 rpc.append("</get-config>\n");
495 rpc.append("</rpc>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800496 rpc.append(ENDPATTERN);
497 String reply = sendRequest(rpc.toString());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800498 return checkReply(reply) ? reply : "ERROR " + reply;
andreaeb70a942015-10-16 21:34:46 -0700499 }
500
501 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800502 public boolean editConfig(String newConfiguration) throws NetconfException {
503 newConfiguration = newConfiguration + ENDPATTERN;
504 return checkReply(sendRequest(newConfiguration));
andreaeb70a942015-10-16 21:34:46 -0700505 }
506
507 @Override
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300508 public boolean editConfig(String netconfTargetConfig, String mode, String newConfiguration)
509 throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700510 return editConfig(TargetConfig.toTargetConfig(netconfTargetConfig), mode, newConfiguration);
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300511 }
512
513 @Override
514 public boolean editConfig(TargetConfig netconfTargetConfig, String mode, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800515 throws NetconfException {
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800516 newConfiguration = newConfiguration.trim();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800517 StringBuilder rpc = new StringBuilder(XML_HEADER);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900518 rpc.append(RPC_OPEN);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800519 rpc.append(MESSAGE_ID_STRING);
520 rpc.append(EQUAL);
521 rpc.append("\"");
522 rpc.append(messageIdInteger.get());
523 rpc.append("\" ");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900524 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
525 rpc.append(EDIT_CONFIG_OPEN).append("\n");
526 rpc.append(TARGET_OPEN);
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300527 rpc.append("<").append(netconfTargetConfig).append("/>");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900528 rpc.append(TARGET_CLOSE).append("\n");
529 if (mode != null) {
530 rpc.append(DEFAULT_OPERATION_OPEN);
531 rpc.append(mode);
532 rpc.append(DEFAULT_OPERATION_CLOSE).append("\n");
533 }
534 rpc.append(CONFIG_OPEN).append("\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800535 rpc.append(newConfiguration);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900536 rpc.append(CONFIG_CLOSE).append("\n");
537 rpc.append(EDIT_CONFIG_CLOSE).append("\n");
538 rpc.append(RPC_CLOSE);
Andrea Campanella101417d2015-12-11 17:58:07 -0800539 rpc.append(ENDPATTERN);
Konstantinos Kanonakis1b8b5592016-09-09 14:34:37 -0500540 log.debug(rpc.toString());
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900541 String reply = sendRequest(rpc.toString());
542 return checkReply(reply);
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800543 }
544
545 @Override
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300546 public boolean copyConfig(String netconfTargetConfig, String newConfiguration) throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700547 return copyConfig(TargetConfig.toTargetConfig(netconfTargetConfig), newConfiguration);
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300548 }
549
550 @Override
551 public boolean copyConfig(TargetConfig netconfTargetConfig, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800552 throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700553 newConfiguration = newConfiguration.trim();
yoonseona1957552016-11-02 15:53:07 -0400554 if (!newConfiguration.startsWith("<config>")) {
555 newConfiguration = "<config>" + newConfiguration
556 + "</config>";
andreaeb70a942015-10-16 21:34:46 -0700557 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800558 StringBuilder rpc = new StringBuilder(XML_HEADER);
yoonseon55faf852016-11-02 14:59:12 -0400559 rpc.append(RPC_OPEN);
560 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
andreaeb70a942015-10-16 21:34:46 -0700561 rpc.append("<copy-config>");
562 rpc.append("<target>");
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300563 rpc.append("<").append(netconfTargetConfig).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700564 rpc.append("</target>");
565 rpc.append("<source>");
yoonseon25e54fc2016-11-02 13:07:43 -0400566 rpc.append(newConfiguration);
andreaeb70a942015-10-16 21:34:46 -0700567 rpc.append("</source>");
568 rpc.append("</copy-config>");
569 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800570 rpc.append(ENDPATTERN);
571 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700572 }
573
574 @Override
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300575 public boolean deleteConfig(String netconfTargetConfig) throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700576 return deleteConfig(TargetConfig.toTargetConfig(netconfTargetConfig));
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300577 }
578
579 @Override
580 public boolean deleteConfig(TargetConfig netconfTargetConfig) throws NetconfException {
Shivani Vaidya48df84e2017-04-13 13:48:17 -0700581 if (netconfTargetConfig.equals(TargetConfig.RUNNING)) {
andreaeb70a942015-10-16 21:34:46 -0700582 log.warn("Target configuration for delete operation can't be \"running\"",
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300583 netconfTargetConfig);
andreaeb70a942015-10-16 21:34:46 -0700584 return false;
585 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800586 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700587 rpc.append("<rpc>");
588 rpc.append("<delete-config>");
589 rpc.append("<target>");
Andrei Mihaescuac542ca2017-03-26 21:36:25 +0300590 rpc.append("<").append(netconfTargetConfig).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700591 rpc.append("</target>");
592 rpc.append("</delete-config>");
593 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800594 rpc.append(ENDPATTERN);
595 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700596 }
597
598 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700599 public boolean lock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800600 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700601 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700602 rpc.append("<lock>");
603 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700604 rpc.append("<");
605 rpc.append(configType);
606 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700607 rpc.append("</target>");
608 rpc.append("</lock>");
609 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800610 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700611 String lockReply = sendRequest(rpc.toString());
612 return checkReply(lockReply);
andreaeb70a942015-10-16 21:34:46 -0700613 }
614
615 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700616 public boolean unlock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800617 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700618 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700619 rpc.append("<unlock>");
620 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700621 rpc.append("<");
622 rpc.append(configType);
623 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700624 rpc.append("</target>");
625 rpc.append("</unlock>");
626 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800627 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700628 String unlockReply = sendRequest(rpc.toString());
629 return checkReply(unlockReply);
630 }
631
632 @Override
633 public boolean lock() throws NetconfException {
634 return lock("running");
635 }
636
637 @Override
638 public boolean unlock() throws NetconfException {
639 return unlock("running");
andreaeb70a942015-10-16 21:34:46 -0700640 }
641
642 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800643 public boolean close() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700644 return close(false);
645 }
646
Andrea Campanella101417d2015-12-11 17:58:07 -0800647 private boolean close(boolean force) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700648 StringBuilder rpc = new StringBuilder();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700649 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
andreaeb70a942015-10-16 21:34:46 -0700650 if (force) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700651 rpc.append("<kill-session/>");
andreaeb70a942015-10-16 21:34:46 -0700652 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700653 rpc.append("<close-session/>");
andreaeb70a942015-10-16 21:34:46 -0700654 }
andreaeb70a942015-10-16 21:34:46 -0700655 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800656 rpc.append(ENDPATTERN);
657 return checkReply(sendRequest(rpc.toString())) || close(true);
andreaeb70a942015-10-16 21:34:46 -0700658 }
659
660 @Override
661 public String getSessionId() {
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800662 return sessionID;
andreaeb70a942015-10-16 21:34:46 -0700663 }
664
665 @Override
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800666 public Set<String> getDeviceCapabilitiesSet() {
667 return Collections.unmodifiableSet(deviceCapabilities);
668 }
669
670 @Deprecated
671 @Override
andreaeb70a942015-10-16 21:34:46 -0700672 public String getServerCapabilities() {
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800673 return serverHelloResponseOld;
674 }
675
676 @Deprecated
677 @Override
678 public void setDeviceCapabilities(List<String> capabilities) {
679 onosCapabilities = capabilities;
andreaeb70a942015-10-16 21:34:46 -0700680 }
681
682 @Override
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800683 public void setOnosCapabilities(Iterable<String> capabilities) {
684 onosCapabilities = capabilities;
andreaeb70a942015-10-16 21:34:46 -0700685 }
686
Aaron Kruglikov72db6422017-02-13 12:16:51 -0800687
Andrea Campanella101417d2015-12-11 17:58:07 -0800688 @Override
689 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700690 streamHandler.addDeviceEventListener(listener);
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700691 primaryListeners.add(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800692 }
693
694 @Override
695 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700696 primaryListeners.remove(listener);
helenyrwu0407c642016-06-09 12:01:30 -0700697 streamHandler.removeDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800698 }
699
700 private boolean checkReply(String reply) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700701 if (reply != null) {
702 if (!reply.contains("<rpc-error>")) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900703 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700704 return true;
705 } else if (reply.contains("<ok/>")
706 || (reply.contains("<rpc-error>")
707 && reply.contains("warning"))) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900708 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700709 return true;
710 }
711 }
Andrea Campanellad264b492016-03-01 09:46:06 -0800712 log.warn("Device {} has error in reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700713 return false;
714 }
715
Yuta HIGUCHI0976bc22017-04-20 16:48:20 -0700716 static class NotificationSession extends NetconfSessionImpl {
717
718 private String notificationFilter;
719
720 NotificationSession(NetconfDeviceInfo deviceInfo)
721 throws NetconfException {
722 super(deviceInfo);
723 }
724
725 @Override
726 protected void startSubscriptionStream(String filterSchema)
727 throws NetconfException {
728
729 notificationFilter = filterSchema;
730 requestSync(createSubscriptionString(filterSchema));
731 }
732
733 @Override
734 public String toString() {
735 return MoreObjects.toStringHelper(getClass())
736 .add("deviceInfo", deviceInfo)
737 .add("sessionID", getSessionId())
738 .add("notificationFilter", notificationFilter)
739 .toString();
740 }
741 }
742
743 /**
744 * Listener attached to child session for notification streaming.
745 *
746 * Forwards all notification event from child session to primary session
747 * listeners.
748 */
749 private final class NotificationForwarder
750 implements NetconfDeviceOutputEventListener {
751
752 @Override
753 public boolean isRelevant(NetconfDeviceOutputEvent event) {
754 return event.type() == Type.DEVICE_NOTIFICATION;
755 }
756
757 @Override
758 public void event(NetconfDeviceOutputEvent event) {
759 primaryListeners.forEach(lsnr -> {
760 if (lsnr.isRelevant(event)) {
761 lsnr.event(event);
762 }
763 });
764 }
765 }
766
Andrea Campanella101417d2015-12-11 17:58:07 -0800767 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
andreaeb70a942015-10-16 21:34:46 -0700768
Andrea Campanella101417d2015-12-11 17:58:07 -0800769 @Override
Andrea Campanellac3627842017-04-04 18:06:54 +0200770 public void notify(NetconfDeviceOutputEvent event) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200771 Optional<Integer> messageId = event.getMessageID();
Andrea Campanella0cee0b62017-04-03 20:02:54 +0200772 log.debug("messageID {}, waiting replies messageIDs {}", messageId,
773 replies.keySet());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200774 if (!messageId.isPresent()) {
775 errorReplies.add(event.getMessagePayload());
Andrea Campanellad264b492016-03-01 09:46:06 -0800776 log.error("Device {} sent error reply {}",
777 event.getDeviceInfo(), event.getMessagePayload());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200778 return;
779 }
780 CompletableFuture<String> completedReply =
781 replies.get(messageId.get());
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900782 if (completedReply != null) {
783 completedReply.complete(event.getMessagePayload());
784 }
andreaeb70a942015-10-16 21:34:46 -0700785 }
786 }
andreaeb70a942015-10-16 21:34:46 -0700787}