blob: 00bbca0c447a9a926fa0b12c408b0dc7bd287c10 [file] [log] [blame]
Yuta HIGUCHI348b3232017-04-20 10:19:48 -07001/*
2 * Copyright 2015-present Open Networking Laboratory
3 *
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
17package org.onosproject.netconf.ctl;
18
19import com.google.common.annotations.Beta;
20import ch.ethz.ssh2.Connection;
21import ch.ethz.ssh2.Session;
22import ch.ethz.ssh2.channel.Channel;
23import com.google.common.base.Preconditions;
24import org.onosproject.netconf.TargetConfig;
25import org.onosproject.netconf.NetconfDeviceInfo;
26import org.onosproject.netconf.NetconfDeviceOutputEvent;
27import org.onosproject.netconf.NetconfDeviceOutputEventListener;
28import org.onosproject.netconf.NetconfException;
29import org.onosproject.netconf.NetconfSession;
30import org.slf4j.Logger;
31import org.slf4j.LoggerFactory;
32
33import java.io.IOException;
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.List;
37import java.util.Map;
38import java.util.Optional;
39import java.util.concurrent.CompletableFuture;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.ExecutionException;
42import java.util.concurrent.TimeUnit;
43import java.util.concurrent.TimeoutException;
44import java.util.concurrent.atomic.AtomicInteger;
45import java.util.regex.Pattern;
46
47
48/**
49 * Implementation of a NETCONF session to talk to a device.
50 *
51 * @deprecated in 1.10.0
52 */
53@Deprecated
54public class NetconfSessionImpl implements NetconfSession {
55
56 private static final Logger log = LoggerFactory
57 .getLogger(NetconfSessionImpl.class);
58
59 private static final String ENDPATTERN = "]]>]]>";
60 private static final String MESSAGE_ID_STRING = "message-id";
61 private static final String HELLO = "<hello";
62 private static final String NEW_LINE = "\n";
63 private static final String END_OF_RPC_OPEN_TAG = "\">";
64 private static final String EQUAL = "=";
65 private static final String NUMBER_BETWEEN_QUOTES_MATCHER = "\"+([0-9]+)+\"";
66 private static final String RPC_OPEN = "<rpc ";
67 private static final String RPC_CLOSE = "</rpc>";
68 private static final String GET_OPEN = "<get>";
69 private static final String GET_CLOSE = "</get>";
70 private static final String WITH_DEFAULT_OPEN = "<with-defaults ";
71 private static final String WITH_DEFAULT_CLOSE = "</with-defaults>";
72 private static final String DEFAULT_OPERATION_OPEN = "<default-operation>";
73 private static final String DEFAULT_OPERATION_CLOSE = "</default-operation>";
74 private static final String SUBTREE_FILTER_OPEN = "<filter type=\"subtree\">";
75 private static final String SUBTREE_FILTER_CLOSE = "</filter>";
76 private static final String EDIT_CONFIG_OPEN = "<edit-config>";
77 private static final String EDIT_CONFIG_CLOSE = "</edit-config>";
78 private static final String TARGET_OPEN = "<target>";
79 private static final String TARGET_CLOSE = "</target>";
80 private static final String CONFIG_OPEN = "<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
81 private static final String CONFIG_CLOSE = "</config>";
82 private static final String XML_HEADER =
83 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
84 private static final String NETCONF_BASE_NAMESPACE =
85 "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"";
86 private static final String NETCONF_WITH_DEFAULTS_NAMESPACE =
87 "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"";
88 private static final String SUBSCRIPTION_SUBTREE_FILTER_OPEN =
89 "<filter xmlns:base10=\"urn:ietf:params:xml:ns:netconf:base:1.0\" base10:type=\"subtree\">";
90
91 private static Pattern msgIdPattern = Pattern.compile("(message-id=\"[0-9]+\")");
92
93 private final AtomicInteger messageIdInteger = new AtomicInteger(0);
94 private Connection netconfConnection;
95 private NetconfDeviceInfo deviceInfo;
96 private Session sshSession;
97 private boolean connectionActive;
98 private List<String> deviceCapabilities =
99 Collections.singletonList("urn:ietf:params:netconf:base:1.0");
100 private String serverCapabilities;
101 private NetconfStreamHandler streamHandler;
102 private Map<Integer, CompletableFuture<String>> replies;
103 private List<String> errorReplies;
104 private boolean subscriptionConnected = false;
105 private String notificationFilterSchema = null;
106
107
108 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
109 this.deviceInfo = deviceInfo;
110 this.netconfConnection = null;
111 this.sshSession = null;
112 connectionActive = false;
113 replies = new ConcurrentHashMap<>();
114 errorReplies = new ArrayList<>();
115 startConnection();
116 }
117
118 private void startConnection() throws NetconfException {
119 if (!connectionActive) {
120 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
121 int connectTimeout = NetconfControllerImpl.netconfConnectTimeout;
122
123 try {
124 netconfConnection.connect(null, 1000 * connectTimeout, 1000 * connectTimeout);
125 } catch (IOException e) {
126 throw new NetconfException("Cannot open a connection with device " + deviceInfo, e);
127 }
128 boolean isAuthenticated;
129 try {
130 if (deviceInfo.getKeyFile() != null && deviceInfo.getKeyFile().canRead()) {
131 log.debug("Authenticating with key file to device {} with username {}",
132 deviceInfo.getDeviceId(), deviceInfo.name());
133 isAuthenticated = netconfConnection.authenticateWithPublicKey(
134 deviceInfo.name(), deviceInfo.getKeyFile(),
135 deviceInfo.password().equals("") ? null : deviceInfo.password());
136 } else if (deviceInfo.getKey() != null) {
137 log.debug("Authenticating with key to device {} with username {}",
138 deviceInfo.getDeviceId(), deviceInfo.name());
139 isAuthenticated = netconfConnection.authenticateWithPublicKey(
140 deviceInfo.name(), deviceInfo.getKey(),
141 deviceInfo.password().equals("") ? null : deviceInfo.password());
142 } else {
143 log.debug("Authenticating to device {} with username {} with password",
144 deviceInfo.getDeviceId(), deviceInfo.name());
145 isAuthenticated = netconfConnection.authenticateWithPassword(
146 deviceInfo.name(), deviceInfo.password());
147 }
148 } catch (IOException e) {
149 log.error("Authentication connection to device {} failed",
150 deviceInfo.getDeviceId(), e);
151 throw new NetconfException("Authentication connection to device " +
152 deviceInfo.getDeviceId() + " failed", e);
153 }
154
155 connectionActive = true;
156 Preconditions.checkArgument(isAuthenticated,
157 "Authentication to device %s with username " +
158 "%s failed",
159 deviceInfo.getDeviceId(), deviceInfo.name());
160 startSshSession();
161 }
162 }
163
164 private void startSshSession() throws NetconfException {
165 try {
166 sshSession = netconfConnection.openSession();
167 sshSession.startSubSystem("netconf");
168 streamHandler = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
169 sshSession.getStderr(), deviceInfo,
170 new NetconfSessionDelegateImpl(),
171 replies);
172 this.addDeviceOutputListener(new NetconfDeviceOutputEventListenerImpl(deviceInfo));
173 sendHello();
174 } catch (IOException e) {
175 log.error("Failed to create ch.ethz.ssh2.Session session {} ", e.getMessage());
176 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
177 deviceInfo, e);
178 }
179 }
180
181
182 @Beta
183 private void startSubscriptionConnection(String filterSchema) throws NetconfException {
184 if (!serverCapabilities.contains("interleave")) {
185 throw new NetconfException("Device" + deviceInfo + "does not support interleave");
186 }
187 String reply = sendRequest(createSubscriptionString(filterSchema));
188 if (!checkReply(reply)) {
189 throw new NetconfException("Subscription not successful with device "
190 + deviceInfo + " with reply " + reply);
191 }
192 subscriptionConnected = true;
193 }
194
195 @Override
196 public void startSubscription() throws NetconfException {
197 if (!subscriptionConnected) {
198 startSubscriptionConnection(null);
199 }
200 streamHandler.setEnableNotifications(true);
201 }
202
203 @Beta
204 @Override
205 public void startSubscription(String filterSchema) throws NetconfException {
206 if (!subscriptionConnected) {
207 notificationFilterSchema = filterSchema;
208 startSubscriptionConnection(filterSchema);
209 }
210 streamHandler.setEnableNotifications(true);
211 }
212
213 @Beta
214 private String createSubscriptionString(String filterSchema) {
215 StringBuilder subscriptionbuffer = new StringBuilder();
216 subscriptionbuffer.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
217 subscriptionbuffer.append(" <create-subscription\n");
218 subscriptionbuffer.append("xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n");
219 // FIXME Only subtree filtering supported at the moment.
220 if (filterSchema != null) {
221 subscriptionbuffer.append(" ");
222 subscriptionbuffer.append(SUBSCRIPTION_SUBTREE_FILTER_OPEN).append(NEW_LINE);
223 subscriptionbuffer.append(filterSchema).append(NEW_LINE);
224 subscriptionbuffer.append(" ");
225 subscriptionbuffer.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
226 }
227 subscriptionbuffer.append(" </create-subscription>\n");
228 subscriptionbuffer.append("</rpc>\n");
229 subscriptionbuffer.append(ENDPATTERN);
230 return subscriptionbuffer.toString();
231 }
232
233 @Override
234 public void endSubscription() throws NetconfException {
235 if (subscriptionConnected) {
236 streamHandler.setEnableNotifications(false);
237 } else {
238 throw new NetconfException("Subscription does not exist.");
239 }
240 }
241
242 private void sendHello() throws NetconfException {
243 serverCapabilities = sendRequest(createHelloString());
244 }
245
246 private String createHelloString() {
247 StringBuilder hellobuffer = new StringBuilder();
248 hellobuffer.append(XML_HEADER);
249 hellobuffer.append("\n");
250 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
251 hellobuffer.append(" <capabilities>\n");
252 deviceCapabilities.forEach(
253 cap -> hellobuffer.append(" <capability>")
254 .append(cap)
255 .append("</capability>\n"));
256 hellobuffer.append(" </capabilities>\n");
257 hellobuffer.append("</hello>\n");
258 hellobuffer.append(ENDPATTERN);
259 return hellobuffer.toString();
260
261 }
262
263 @Override
264 public void checkAndReestablish() throws NetconfException {
265 if (sshSession.getState() != Channel.STATE_OPEN) {
266 try {
267 log.debug("Trying to reopen the Sesion with {}", deviceInfo.getDeviceId());
268 startSshSession();
269 } catch (IOException | IllegalStateException e) {
270 log.debug("Trying to reopen the Connection with {}", deviceInfo.getDeviceId());
271 try {
272 connectionActive = false;
273 replies.clear();
274 messageIdInteger.set(0);
275 startConnection();
276 if (subscriptionConnected) {
277 log.debug("Restarting subscription with {}", deviceInfo.getDeviceId());
278 subscriptionConnected = false;
279 startSubscription(notificationFilterSchema);
280 }
281 } catch (IOException e2) {
282 log.error("No connection {} for device {}", netconfConnection, e.getMessage());
283 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
284 }
285 }
286 }
287 }
288
289 @Override
290 public String requestSync(String request) throws NetconfException {
291 if (!request.contains(ENDPATTERN)) {
292 request = request + NEW_LINE + ENDPATTERN;
293 }
294 String reply = sendRequest(request);
295 checkReply(reply);
296 return reply;
297 }
298
299 @Override
300 @Deprecated
301 public CompletableFuture<String> request(String request) {
302 return streamHandler.sendMessage(request);
303 }
304
305 private CompletableFuture<String> request(String request, int messageId) {
306 return streamHandler.sendMessage(request, messageId);
307 }
308
309 private String sendRequest(String request) throws NetconfException {
310 checkAndReestablish();
311 final int messageId = messageIdInteger.getAndIncrement();
312 request = formatRequestMessageId(request, messageId);
313 request = formatXmlHeader(request);
314 CompletableFuture<String> futureReply = request(request, messageId);
315 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
316 String rp;
317 try {
318 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
319 replies.remove(messageId);
320 } catch (InterruptedException | ExecutionException | TimeoutException e) {
321 throw new NetconfException("No matching reply for request " + request, e);
322 }
323 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
324 return rp.trim();
325 }
326
327 private String formatRequestMessageId(String request, int messageId) {
328 if (request.contains(MESSAGE_ID_STRING)) {
329 //FIXME if application provides his own counting of messages this fails that count
330 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
331 MESSAGE_ID_STRING + EQUAL + "\"" + messageId + "\"");
332 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
333 //FIXME find out a better way to enforce the presence of message-id
334 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
335 + messageId + "\"" + ">");
336 }
337 return request;
338 }
339
340 private String formatXmlHeader(String request) {
341 if (!request.contains(XML_HEADER)) {
342 //FIXME if application provieds his own XML header of different type there is a clash
343 request = XML_HEADER + "\n" + request;
344 }
345 return request;
346 }
347
348 @Override
349 public String doWrappedRpc(String request) throws NetconfException {
350 StringBuilder rpc = new StringBuilder(XML_HEADER);
351 rpc.append(RPC_OPEN);
352 rpc.append(MESSAGE_ID_STRING);
353 rpc.append(EQUAL);
354 rpc.append("\"");
355 rpc.append(messageIdInteger.get());
356 rpc.append("\" ");
357 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
358 rpc.append(request);
359 rpc.append(RPC_CLOSE).append(NEW_LINE);
360 rpc.append(ENDPATTERN);
361 String reply = sendRequest(rpc.toString());
362 checkReply(reply);
363 return reply;
364 }
365
366 @Override
367 public String get(String request) throws NetconfException {
368 return requestSync(request);
369 }
370
371 @Override
372 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
373 StringBuilder rpc = new StringBuilder(XML_HEADER);
374 rpc.append(RPC_OPEN);
375 rpc.append(MESSAGE_ID_STRING);
376 rpc.append(EQUAL);
377 rpc.append("\"");
378 rpc.append(messageIdInteger.get());
379 rpc.append("\" ");
380 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
381 rpc.append(GET_OPEN).append(NEW_LINE);
382 if (filterSchema != null) {
383 rpc.append(SUBTREE_FILTER_OPEN).append(NEW_LINE);
384 rpc.append(filterSchema).append(NEW_LINE);
385 rpc.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
386 }
387 if (withDefaultsMode != null) {
388 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
389 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
390 }
391 rpc.append(GET_CLOSE).append(NEW_LINE);
392 rpc.append(RPC_CLOSE).append(NEW_LINE);
393 rpc.append(ENDPATTERN);
394 String reply = sendRequest(rpc.toString());
395 checkReply(reply);
396 return reply;
397 }
398
399 @Override
400 public String getConfig(TargetConfig netconfTargetConfig) throws NetconfException {
401 return getConfig(netconfTargetConfig, null);
402 }
403
404 @Override
405 public String getConfig(String netconfTargetConfig) throws NetconfException {
406 return getConfig(TargetConfig.toTargetConfig(netconfTargetConfig));
407 }
408
409 @Override
410 public String getConfig(String netconfTargetConfig, String configurationFilterSchema) throws NetconfException {
411 return getConfig(TargetConfig.toTargetConfig(netconfTargetConfig), configurationFilterSchema);
412 }
413
414 @Override
415 public String getConfig(TargetConfig netconfTargetConfig, String configurationSchema) throws NetconfException {
416 StringBuilder rpc = new StringBuilder(XML_HEADER);
417 rpc.append("<rpc ");
418 rpc.append(MESSAGE_ID_STRING);
419 rpc.append(EQUAL);
420 rpc.append("\"");
421 rpc.append(messageIdInteger.get());
422 rpc.append("\" ");
423 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
424 rpc.append("<get-config>\n");
425 rpc.append("<source>\n");
426 rpc.append("<").append(netconfTargetConfig).append("/>");
427 rpc.append("</source>");
428 if (configurationSchema != null) {
429 rpc.append("<filter type=\"subtree\">\n");
430 rpc.append(configurationSchema).append("\n");
431 rpc.append("</filter>\n");
432 }
433 rpc.append("</get-config>\n");
434 rpc.append("</rpc>\n");
435 rpc.append(ENDPATTERN);
436 String reply = sendRequest(rpc.toString());
437 return checkReply(reply) ? reply : "ERROR " + reply;
438 }
439
440 @Override
441 public boolean editConfig(String newConfiguration) throws NetconfException {
442 newConfiguration = newConfiguration + ENDPATTERN;
443 return checkReply(sendRequest(newConfiguration));
444 }
445
446 @Override
447 public boolean editConfig(String netconfTargetConfig, String mode, String newConfiguration)
448 throws NetconfException {
449 return editConfig(TargetConfig.toTargetConfig(netconfTargetConfig), mode, newConfiguration);
450 }
451
452 @Override
453 public boolean editConfig(TargetConfig netconfTargetConfig, String mode, String newConfiguration)
454 throws NetconfException {
455 newConfiguration = newConfiguration.trim();
456 StringBuilder rpc = new StringBuilder(XML_HEADER);
457 rpc.append(RPC_OPEN);
458 rpc.append(MESSAGE_ID_STRING);
459 rpc.append(EQUAL);
460 rpc.append("\"");
461 rpc.append(messageIdInteger.get());
462 rpc.append("\" ");
463 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
464 rpc.append(EDIT_CONFIG_OPEN).append("\n");
465 rpc.append(TARGET_OPEN);
466 rpc.append("<").append(netconfTargetConfig).append("/>");
467 rpc.append(TARGET_CLOSE).append("\n");
468 if (mode != null) {
469 rpc.append(DEFAULT_OPERATION_OPEN);
470 rpc.append(mode);
471 rpc.append(DEFAULT_OPERATION_CLOSE).append("\n");
472 }
473 rpc.append(CONFIG_OPEN).append("\n");
474 rpc.append(newConfiguration);
475 rpc.append(CONFIG_CLOSE).append("\n");
476 rpc.append(EDIT_CONFIG_CLOSE).append("\n");
477 rpc.append(RPC_CLOSE);
478 rpc.append(ENDPATTERN);
479 log.debug(rpc.toString());
480 String reply = sendRequest(rpc.toString());
481 return checkReply(reply);
482 }
483
484 @Override
485 public boolean copyConfig(String netconfTargetConfig, String newConfiguration) throws NetconfException {
486 return copyConfig(TargetConfig.toTargetConfig(netconfTargetConfig), newConfiguration);
487 }
488
489 @Override
490 public boolean copyConfig(TargetConfig netconfTargetConfig, String newConfiguration)
491 throws NetconfException {
492 newConfiguration = newConfiguration.trim();
493 if (!newConfiguration.startsWith("<config>")) {
494 newConfiguration = "<config>" + newConfiguration
495 + "</config>";
496 }
497 StringBuilder rpc = new StringBuilder(XML_HEADER);
498 rpc.append(RPC_OPEN);
499 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
500 rpc.append("<copy-config>");
501 rpc.append("<target>");
502 rpc.append("<").append(netconfTargetConfig).append("/>");
503 rpc.append("</target>");
504 rpc.append("<source>");
505 rpc.append(newConfiguration);
506 rpc.append("</source>");
507 rpc.append("</copy-config>");
508 rpc.append("</rpc>");
509 rpc.append(ENDPATTERN);
510 return checkReply(sendRequest(rpc.toString()));
511 }
512
513 @Override
514 public boolean deleteConfig(String netconfTargetConfig) throws NetconfException {
515 return deleteConfig(TargetConfig.toTargetConfig(netconfTargetConfig));
516 }
517
518 @Override
519 public boolean deleteConfig(TargetConfig netconfTargetConfig) throws NetconfException {
520 if (netconfTargetConfig.equals(TargetConfig.RUNNING)) {
521 log.warn("Target configuration for delete operation can't be \"running\"",
522 netconfTargetConfig);
523 return false;
524 }
525 StringBuilder rpc = new StringBuilder(XML_HEADER);
526 rpc.append("<rpc>");
527 rpc.append("<delete-config>");
528 rpc.append("<target>");
529 rpc.append("<").append(netconfTargetConfig).append("/>");
530 rpc.append("</target>");
531 rpc.append("</delete-config>");
532 rpc.append("</rpc>");
533 rpc.append(ENDPATTERN);
534 return checkReply(sendRequest(rpc.toString()));
535 }
536
537 @Override
538 public boolean lock(String configType) throws NetconfException {
539 StringBuilder rpc = new StringBuilder(XML_HEADER);
540 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
541 rpc.append("<lock>");
542 rpc.append("<target>");
543 rpc.append("<");
544 rpc.append(configType);
545 rpc.append("/>");
546 rpc.append("</target>");
547 rpc.append("</lock>");
548 rpc.append("</rpc>");
549 rpc.append(ENDPATTERN);
550 String lockReply = sendRequest(rpc.toString());
551 return checkReply(lockReply);
552 }
553
554 @Override
555 public boolean unlock(String configType) throws NetconfException {
556 StringBuilder rpc = new StringBuilder(XML_HEADER);
557 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
558 rpc.append("<unlock>");
559 rpc.append("<target>");
560 rpc.append("<");
561 rpc.append(configType);
562 rpc.append("/>");
563 rpc.append("</target>");
564 rpc.append("</unlock>");
565 rpc.append("</rpc>");
566 rpc.append(ENDPATTERN);
567 String unlockReply = sendRequest(rpc.toString());
568 return checkReply(unlockReply);
569 }
570
571 @Override
572 public boolean lock() throws NetconfException {
573 return lock("running");
574 }
575
576 @Override
577 public boolean unlock() throws NetconfException {
578 return unlock("running");
579 }
580
581 @Override
582 public boolean close() throws NetconfException {
583 return close(false);
584 }
585
586 private boolean close(boolean force) throws NetconfException {
587 StringBuilder rpc = new StringBuilder();
588 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
589 if (force) {
590 rpc.append("<kill-session/>");
591 } else {
592 rpc.append("<close-session/>");
593 }
594 rpc.append("</rpc>");
595 rpc.append(ENDPATTERN);
596 return checkReply(sendRequest(rpc.toString())) || close(true);
597 }
598
599 @Override
600 public String getSessionId() {
601 if (serverCapabilities.contains("<session-id>")) {
602 String[] outer = serverCapabilities.split("<session-id>");
603 Preconditions.checkArgument(outer.length != 1,
604 "Error in retrieving the session id");
605 String[] value = outer[1].split("</session-id>");
606 Preconditions.checkArgument(value.length != 1,
607 "Error in retrieving the session id");
608 return value[0];
609 } else {
610 return String.valueOf(-1);
611 }
612 }
613
614 @Override
615 public String getServerCapabilities() {
616 return serverCapabilities;
617 }
618
619 @Override
620 public void setDeviceCapabilities(List<String> capabilities) {
621 deviceCapabilities = capabilities;
622 }
623
624 @Override
625 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
626 streamHandler.addDeviceEventListener(listener);
627 }
628
629 @Override
630 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
631 streamHandler.removeDeviceEventListener(listener);
632 }
633
634 private boolean checkReply(String reply) throws NetconfException {
635 if (reply != null) {
636 if (!reply.contains("<rpc-error>")) {
637 log.debug("Device {} sent reply {}", deviceInfo, reply);
638 return true;
639 } else if (reply.contains("<ok/>")
640 || (reply.contains("<rpc-error>")
641 && reply.contains("warning"))) {
642 log.debug("Device {} sent reply {}", deviceInfo, reply);
643 return true;
644 }
645 }
646 log.warn("Device {} has error in reply {}", deviceInfo, reply);
647 return false;
648 }
649
650 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
651
652 @Override
653 public void notify(NetconfDeviceOutputEvent event) {
654 Optional<Integer> messageId = event.getMessageID();
655 log.debug("messageID {}, waiting replies messageIDs {}", messageId,
656 replies.keySet());
657 if (!messageId.isPresent()) {
658 errorReplies.add(event.getMessagePayload());
659 log.error("Device {} sent error reply {}",
660 event.getDeviceInfo(), event.getMessagePayload());
661 return;
662 }
663 CompletableFuture<String> completedReply =
664 replies.get(messageId.get());
665 if (completedReply != null) {
666 completedReply.complete(event.getMessagePayload());
667 }
668 }
669 }
670}