blob: 451a2a1c1cca54b83490a30f5bf62332115ae071 [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
17package org.onosproject.netconf.ctl;
18
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;
22import com.google.common.base.Preconditions;
23import org.onosproject.netconf.NetconfDeviceInfo;
Andrea Campanella101417d2015-12-11 17:58:07 -080024import org.onosproject.netconf.NetconfDeviceOutputEvent;
25import org.onosproject.netconf.NetconfDeviceOutputEventListener;
26import org.onosproject.netconf.NetconfException;
andreaeb70a942015-10-16 21:34:46 -070027import org.onosproject.netconf.NetconfSession;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
andreaeb70a942015-10-16 21:34:46 -070031import java.io.IOException;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020032import java.util.ArrayList;
Andrea Campanella1cd641b2015-12-07 17:28:34 -080033import java.util.Collections;
Andrea Campanella101417d2015-12-11 17:58:07 -080034import java.util.HashMap;
andreaeb70a942015-10-16 21:34:46 -070035import java.util.List;
Andrea Campanella101417d2015-12-11 17:58:07 -080036import java.util.Map;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020037import java.util.Optional;
Andrea Campanella101417d2015-12-11 17:58:07 -080038import java.util.concurrent.CompletableFuture;
Andrea Campanellab029b9e2016-01-29 11:05:36 -080039import java.util.concurrent.ExecutionException;
40import java.util.concurrent.TimeUnit;
41import java.util.concurrent.TimeoutException;
Andrea Campanella101417d2015-12-11 17:58:07 -080042import java.util.concurrent.atomic.AtomicInteger;
43
andreaeb70a942015-10-16 21:34:46 -070044
45/**
46 * Implementation of a NETCONF session to talk to a device.
47 */
48public class NetconfSessionImpl implements NetconfSession {
49
Andrea Campanella101417d2015-12-11 17:58:07 -080050 private static final Logger log = LoggerFactory
andreaeb70a942015-10-16 21:34:46 -070051 .getLogger(NetconfSessionImpl.class);
Andrea Campanella101417d2015-12-11 17:58:07 -080052
Andrea Campanella101417d2015-12-11 17:58:07 -080053 private static final String ENDPATTERN = "]]>]]>";
Andrea Campanella101417d2015-12-11 17:58:07 -080054 private static final String MESSAGE_ID_STRING = "message-id";
Andrea Campanella1311ea02016-03-04 17:51:25 -080055 private static final String HELLO = "<hello";
Andrea Campanella101417d2015-12-11 17:58:07 -080056 private static final String NEW_LINE = "\n";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080057 private static final String END_OF_RPC_OPEN_TAG = "\">";
58 private static final String EQUAL = "=";
59 private static final String NUMBER_BETWEEN_QUOTES_MATCHER = "\"+([0-9]+)+\"";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090060 private static final String RPC_OPEN = "<rpc ";
61 private static final String RPC_CLOSE = "</rpc>";
62 private static final String GET_OPEN = "<get>";
63 private static final String GET_CLOSE = "</get>";
64 private static final String WITH_DEFAULT_OPEN = "<with-defaults ";
65 private static final String WITH_DEFAULT_CLOSE = "</with-defaults>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090066 private static final String DEFAULT_OPERATION_OPEN = "<default-operation>";
67 private static final String DEFAULT_OPERATION_CLOSE = "</default-operation>";
Akihiro Yamanouchi45122222016-07-15 13:13:11 +090068 private static final String SUBTREE_FILTER_OPEN = "<filter type=\"subtree\">";
69 private static final String SUBTREE_FILTER_CLOSE = "</filter>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090070 private static final String EDIT_CONFIG_OPEN = "<edit-config>";
71 private static final String EDIT_CONFIG_CLOSE = "</edit-config>";
72 private static final String TARGET_OPEN = "<target>";
73 private static final String TARGET_CLOSE = "</target>";
Sean Condonb0720e72017-01-10 12:29:02 +000074 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 +090075 private static final String CONFIG_CLOSE = "</config>";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080076 private static final String XML_HEADER =
77 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090078 private static final String NETCONF_BASE_NAMESPACE =
79 "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"";
80 private static final String NETCONF_WITH_DEFAULTS_NAMESPACE =
81 "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"";
Akihiro Yamanouchi45122222016-07-15 13:13:11 +090082 private static final String SUBSCRIPTION_SUBTREE_FILTER_OPEN =
83 "<filter xmlns:base10=\"urn:ietf:params:xml:ns:netconf:base:1.0\" base10:type=\"subtree\">";
andreaeb70a942015-10-16 21:34:46 -070084
Andrea Campanellab029b9e2016-01-29 11:05:36 -080085 private final AtomicInteger messageIdInteger = new AtomicInteger(0);
andreaeb70a942015-10-16 21:34:46 -070086 private Connection netconfConnection;
87 private NetconfDeviceInfo deviceInfo;
88 private Session sshSession;
89 private boolean connectionActive;
andreaeb70a942015-10-16 21:34:46 -070090 private List<String> deviceCapabilities =
Andrea Campanella1cd641b2015-12-07 17:28:34 -080091 Collections.singletonList("urn:ietf:params:netconf:base:1.0");
andreaeb70a942015-10-16 21:34:46 -070092 private String serverCapabilities;
helenyrwu0407c642016-06-09 12:01:30 -070093 private NetconfStreamHandler streamHandler;
Andrea Campanella101417d2015-12-11 17:58:07 -080094 private Map<Integer, CompletableFuture<String>> replies;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020095 private List<String> errorReplies;
helenyrwu0407c642016-06-09 12:01:30 -070096 private boolean subscriptionConnected = false;
andreaeb70a942015-10-16 21:34:46 -070097
98
Andrea Campanella101417d2015-12-11 17:58:07 -080099 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700100 this.deviceInfo = deviceInfo;
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900101 this.netconfConnection = null;
102 this.sshSession = null;
andreaeb70a942015-10-16 21:34:46 -0700103 connectionActive = false;
Andrea Campanella101417d2015-12-11 17:58:07 -0800104 replies = new HashMap<>();
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200105 errorReplies = new ArrayList<>();
andreaeb70a942015-10-16 21:34:46 -0700106 startConnection();
107 }
108
Andrea Campanella101417d2015-12-11 17:58:07 -0800109 private void startConnection() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700110 if (!connectionActive) {
111 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
Sean Condon334ad692016-12-13 17:56:56 +0000112 int connectTimeout = NetconfControllerImpl.netconfConnectTimeout;
113
Andrea Campanella101417d2015-12-11 17:58:07 -0800114 try {
Sean Condon334ad692016-12-13 17:56:56 +0000115 netconfConnection.connect(null, 1000 * connectTimeout, 1000 * connectTimeout);
Andrea Campanella101417d2015-12-11 17:58:07 -0800116 } catch (IOException e) {
117 throw new NetconfException("Cannot open a connection with device" + deviceInfo, e);
118 }
andreaeb70a942015-10-16 21:34:46 -0700119 boolean isAuthenticated;
120 try {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600121 if (deviceInfo.getKey() != null) {
122 log.debug("Authenticating with key to device {} with username {}",
123 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700124 isAuthenticated = netconfConnection.authenticateWithPublicKey(
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600125 deviceInfo.name(), deviceInfo.getKey(),
126 deviceInfo.password().equals("") ? null : deviceInfo.password());
andreaeb70a942015-10-16 21:34:46 -0700127 } else {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600128 log.debug("Authenticating to device {} with username {} with password",
Andrea Campanella50d25212016-02-26 13:06:23 -0800129 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700130 isAuthenticated = netconfConnection.authenticateWithPassword(
131 deviceInfo.name(), deviceInfo.password());
132 }
133 } catch (IOException e) {
Ryan Goulding69524392016-12-03 13:21:20 -0500134 log.error("Authentication connection to device {} failed",
135 deviceInfo.getDeviceId(), e);
Andrea Campanella101417d2015-12-11 17:58:07 -0800136 throw new NetconfException("Authentication connection to device " +
137 deviceInfo.getDeviceId() + " failed", e);
andreaeb70a942015-10-16 21:34:46 -0700138 }
139
140 connectionActive = true;
141 Preconditions.checkArgument(isAuthenticated,
Andrea Campanellad264b492016-03-01 09:46:06 -0800142 "Authentication to device %s with username " +
143 "%s failed",
Andrea Campanella50d25212016-02-26 13:06:23 -0800144 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700145 startSshSession();
146 }
147 }
148
Andrea Campanella101417d2015-12-11 17:58:07 -0800149 private void startSshSession() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700150 try {
151 sshSession = netconfConnection.openSession();
152 sshSession.startSubSystem("netconf");
helenyrwu0407c642016-06-09 12:01:30 -0700153 streamHandler = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
154 sshSession.getStderr(), deviceInfo,
155 new NetconfSessionDelegateImpl());
Andrea Campanella101417d2015-12-11 17:58:07 -0800156 this.addDeviceOutputListener(new NetconfDeviceOutputEventListenerImpl(deviceInfo));
andreaeb70a942015-10-16 21:34:46 -0700157 sendHello();
158 } catch (IOException e) {
Ryan Goulding69524392016-12-03 13:21:20 -0500159 log.error("Failed to create ch.ethz.ssh2.Session session.", e);
Andrea Campanella101417d2015-12-11 17:58:07 -0800160 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
161 deviceInfo, e);
andreaeb70a942015-10-16 21:34:46 -0700162 }
163 }
164
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900165
166 @Beta
167 private void startSubscriptionConnection(String filterSchema) throws NetconfException {
helenyrwu0407c642016-06-09 12:01:30 -0700168 if (!serverCapabilities.contains("interleave")) {
169 throw new NetconfException("Device" + deviceInfo + "does not support interleave");
170 }
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900171 String reply = sendRequest(createSubscriptionString(filterSchema));
helenyrwu0407c642016-06-09 12:01:30 -0700172 if (!checkReply(reply)) {
173 throw new NetconfException("Subscription not successful with device "
174 + deviceInfo + " with reply " + reply);
175 }
176 subscriptionConnected = true;
177 }
178
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900179 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700180 public void startSubscription() throws NetconfException {
181 if (!subscriptionConnected) {
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900182 startSubscriptionConnection(null);
helenyrwu0407c642016-06-09 12:01:30 -0700183 }
184 streamHandler.setEnableNotifications(true);
185 }
186
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900187 @Beta
188 @Override
189 public void startSubscription(String filterSchema) throws NetconfException {
190 if (!subscriptionConnected) {
191 startSubscriptionConnection(filterSchema);
192 }
193 streamHandler.setEnableNotifications(true);
194 }
195
196 @Beta
197 private String createSubscriptionString(String filterSchema) {
helenyrwu0407c642016-06-09 12:01:30 -0700198 StringBuilder subscriptionbuffer = new StringBuilder();
199 subscriptionbuffer.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
200 subscriptionbuffer.append(" <create-subscription\n");
201 subscriptionbuffer.append("xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n");
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900202 // FIXME Only subtree filtering supported at the moment.
203 if (filterSchema != null) {
204 subscriptionbuffer.append(" ");
205 subscriptionbuffer.append(SUBSCRIPTION_SUBTREE_FILTER_OPEN).append(NEW_LINE);
206 subscriptionbuffer.append(filterSchema).append(NEW_LINE);
207 subscriptionbuffer.append(" ");
208 subscriptionbuffer.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
209 }
helenyrwu0407c642016-06-09 12:01:30 -0700210 subscriptionbuffer.append(" </create-subscription>\n");
211 subscriptionbuffer.append("</rpc>\n");
212 subscriptionbuffer.append(ENDPATTERN);
213 return subscriptionbuffer.toString();
214 }
215
216 @Override
217 public void endSubscription() throws NetconfException {
218 if (subscriptionConnected) {
219 streamHandler.setEnableNotifications(false);
220 } else {
221 throw new NetconfException("Subscription does not exist.");
222 }
223 }
224
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800225 private void sendHello() throws NetconfException {
Andrea Campanella101417d2015-12-11 17:58:07 -0800226 serverCapabilities = sendRequest(createHelloString());
andreaeb70a942015-10-16 21:34:46 -0700227 }
228
229 private String createHelloString() {
230 StringBuilder hellobuffer = new StringBuilder();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800231 hellobuffer.append(XML_HEADER);
232 hellobuffer.append("\n");
andreaeb70a942015-10-16 21:34:46 -0700233 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
234 hellobuffer.append(" <capabilities>\n");
235 deviceCapabilities.forEach(
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800236 cap -> hellobuffer.append(" <capability>")
237 .append(cap)
238 .append("</capability>\n"));
andreaeb70a942015-10-16 21:34:46 -0700239 hellobuffer.append(" </capabilities>\n");
240 hellobuffer.append("</hello>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800241 hellobuffer.append(ENDPATTERN);
andreaeb70a942015-10-16 21:34:46 -0700242 return hellobuffer.toString();
243
244 }
245
Andrea Campanella101417d2015-12-11 17:58:07 -0800246 private void checkAndRestablishSession() throws NetconfException {
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800247 if (sshSession.getState() != 2) {
248 try {
249 startSshSession();
250 } catch (IOException e) {
Ryan Goulding69524392016-12-03 13:21:20 -0500251 log.debug("The connection with {} was reopened", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800252 try {
253 startConnection();
254 } catch (IOException e2) {
Andrea Campanella50d25212016-02-26 13:06:23 -0800255 log.error("No connection {} for device", netconfConnection, e2);
Andrea Campanella101417d2015-12-11 17:58:07 -0800256 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800257 }
258 }
259 }
260 }
261
andreaeb70a942015-10-16 21:34:46 -0700262 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800263 public String requestSync(String request) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800264 if (!request.contains(ENDPATTERN)) {
265 request = request + NEW_LINE + ENDPATTERN;
266 }
267 String reply = sendRequest(request);
Andreas Papazois2e557be2016-06-04 15:39:56 +0300268 checkReply(reply);
269 return reply;
andreaeb70a942015-10-16 21:34:46 -0700270 }
271
272 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800273 public CompletableFuture<String> request(String request) {
helenyrwu0407c642016-06-09 12:01:30 -0700274 CompletableFuture<String> ftrep = streamHandler.sendMessage(request);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800275 replies.put(messageIdInteger.get(), ftrep);
Andrea Campanella101417d2015-12-11 17:58:07 -0800276 return ftrep;
277 }
278
279 private String sendRequest(String request) throws NetconfException {
280 checkAndRestablishSession();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800281 request = formatRequestMessageId(request);
282 request = formatXmlHeader(request);
Andrea Campanella101417d2015-12-11 17:58:07 -0800283 CompletableFuture<String> futureReply = request(request);
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200284 messageIdInteger.incrementAndGet();
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300285 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800286 String rp;
287 try {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300288 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800289 } catch (InterruptedException | ExecutionException | TimeoutException e) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200290 throw new NetconfException("No matching reply for request " + request, e);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800291 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800292 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
Andrea Campanella50d25212016-02-26 13:06:23 -0800293 return rp.trim();
Andrea Campanella101417d2015-12-11 17:58:07 -0800294 }
295
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800296 private String formatRequestMessageId(String request) {
297 if (request.contains(MESSAGE_ID_STRING)) {
298 //FIXME if application provieds his own counting of messages this fails that count
299 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
300 MESSAGE_ID_STRING + EQUAL + "\"" + messageIdInteger.get() + "\"");
301 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
302 //FIXME find out a better way to enforce the presence of message-id
303 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
304 + messageIdInteger.get() + "\"" + ">");
305 }
306 return request;
307 }
308
309 private String formatXmlHeader(String request) {
310 if (!request.contains(XML_HEADER)) {
311 //FIXME if application provieds his own XML header of different type there is a clash
312 request = XML_HEADER + "\n" + request;
313 }
314 return request;
315 }
316
Andrea Campanella101417d2015-12-11 17:58:07 -0800317 @Override
Akihiro Yamanouchi8d3a9d32016-07-12 11:41:44 +0900318 public String doWrappedRpc(String request) throws NetconfException {
319 StringBuilder rpc = new StringBuilder(XML_HEADER);
320 rpc.append(RPC_OPEN);
321 rpc.append(MESSAGE_ID_STRING);
322 rpc.append(EQUAL);
323 rpc.append("\"");
324 rpc.append(messageIdInteger.get());
325 rpc.append("\" ");
326 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
327 rpc.append(request);
328 rpc.append(RPC_CLOSE).append(NEW_LINE);
329 rpc.append(ENDPATTERN);
330 String reply = sendRequest(rpc.toString());
331 checkReply(reply);
332 return reply;
333 }
334
335 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800336 public String get(String request) throws NetconfException {
337 return requestSync(request);
338 }
339
340 @Override
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900341 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
342 StringBuilder rpc = new StringBuilder(XML_HEADER);
343 rpc.append(RPC_OPEN);
344 rpc.append(MESSAGE_ID_STRING);
345 rpc.append(EQUAL);
346 rpc.append("\"");
347 rpc.append(messageIdInteger.get());
348 rpc.append("\" ");
349 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
350 rpc.append(GET_OPEN).append(NEW_LINE);
351 if (filterSchema != null) {
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900352 rpc.append(SUBTREE_FILTER_OPEN).append(NEW_LINE);
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900353 rpc.append(filterSchema).append(NEW_LINE);
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900354 rpc.append(SUBTREE_FILTER_CLOSE).append(NEW_LINE);
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900355 }
356 if (withDefaultsMode != null) {
357 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
358 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
359 }
360 rpc.append(GET_CLOSE).append(NEW_LINE);
361 rpc.append(RPC_CLOSE).append(NEW_LINE);
362 rpc.append(ENDPATTERN);
363 String reply = sendRequest(rpc.toString());
364 checkReply(reply);
365 return reply;
366 }
367
368 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800369 public String getConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700370 return getConfig(targetConfiguration, null);
371 }
372
373 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800374 public String getConfig(String targetConfiguration, String configurationSchema) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800375 StringBuilder rpc = new StringBuilder(XML_HEADER);
376 rpc.append("<rpc ");
377 rpc.append(MESSAGE_ID_STRING);
378 rpc.append(EQUAL);
379 rpc.append("\"");
380 rpc.append(messageIdInteger.get());
381 rpc.append("\" ");
382 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700383 rpc.append("<get-config>\n");
384 rpc.append("<source>\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800385 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700386 rpc.append("</source>");
387 if (configurationSchema != null) {
388 rpc.append("<filter type=\"subtree\">\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800389 rpc.append(configurationSchema).append("\n");
andreaeb70a942015-10-16 21:34:46 -0700390 rpc.append("</filter>\n");
391 }
392 rpc.append("</get-config>\n");
393 rpc.append("</rpc>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800394 rpc.append(ENDPATTERN);
395 String reply = sendRequest(rpc.toString());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800396 return checkReply(reply) ? reply : "ERROR " + reply;
andreaeb70a942015-10-16 21:34:46 -0700397 }
398
399 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800400 public boolean editConfig(String newConfiguration) throws NetconfException {
401 newConfiguration = newConfiguration + ENDPATTERN;
402 return checkReply(sendRequest(newConfiguration));
andreaeb70a942015-10-16 21:34:46 -0700403 }
404
405 @Override
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800406 public boolean editConfig(String targetConfiguration, String mode, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800407 throws NetconfException {
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800408 newConfiguration = newConfiguration.trim();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800409 StringBuilder rpc = new StringBuilder(XML_HEADER);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900410 rpc.append(RPC_OPEN);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800411 rpc.append(MESSAGE_ID_STRING);
412 rpc.append(EQUAL);
413 rpc.append("\"");
414 rpc.append(messageIdInteger.get());
415 rpc.append("\" ");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900416 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
417 rpc.append(EDIT_CONFIG_OPEN).append("\n");
418 rpc.append(TARGET_OPEN);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800419 rpc.append("<").append(targetConfiguration).append("/>");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900420 rpc.append(TARGET_CLOSE).append("\n");
421 if (mode != null) {
422 rpc.append(DEFAULT_OPERATION_OPEN);
423 rpc.append(mode);
424 rpc.append(DEFAULT_OPERATION_CLOSE).append("\n");
425 }
426 rpc.append(CONFIG_OPEN).append("\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800427 rpc.append(newConfiguration);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900428 rpc.append(CONFIG_CLOSE).append("\n");
429 rpc.append(EDIT_CONFIG_CLOSE).append("\n");
430 rpc.append(RPC_CLOSE);
Andrea Campanella101417d2015-12-11 17:58:07 -0800431 rpc.append(ENDPATTERN);
Konstantinos Kanonakis1b8b5592016-09-09 14:34:37 -0500432 log.debug(rpc.toString());
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900433 String reply = sendRequest(rpc.toString());
434 return checkReply(reply);
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800435 }
436
437 @Override
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800438 public boolean copyConfig(String targetConfiguration, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800439 throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700440 newConfiguration = newConfiguration.trim();
yoonseona1957552016-11-02 15:53:07 -0400441 if (!newConfiguration.startsWith("<config>")) {
442 newConfiguration = "<config>" + newConfiguration
443 + "</config>";
andreaeb70a942015-10-16 21:34:46 -0700444 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800445 StringBuilder rpc = new StringBuilder(XML_HEADER);
yoonseon55faf852016-11-02 14:59:12 -0400446 rpc.append(RPC_OPEN);
447 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
andreaeb70a942015-10-16 21:34:46 -0700448 rpc.append("<copy-config>");
449 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800450 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700451 rpc.append("</target>");
452 rpc.append("<source>");
yoonseon25e54fc2016-11-02 13:07:43 -0400453 rpc.append(newConfiguration);
andreaeb70a942015-10-16 21:34:46 -0700454 rpc.append("</source>");
455 rpc.append("</copy-config>");
456 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800457 rpc.append(ENDPATTERN);
458 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700459 }
460
461 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800462 public boolean deleteConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700463 if (targetConfiguration.equals("running")) {
464 log.warn("Target configuration for delete operation can't be \"running\"",
465 targetConfiguration);
466 return false;
467 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800468 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700469 rpc.append("<rpc>");
470 rpc.append("<delete-config>");
471 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800472 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700473 rpc.append("</target>");
474 rpc.append("</delete-config>");
475 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800476 rpc.append(ENDPATTERN);
477 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700478 }
479
480 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700481 public boolean lock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800482 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700483 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700484 rpc.append("<lock>");
485 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700486 rpc.append("<");
487 rpc.append(configType);
488 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700489 rpc.append("</target>");
490 rpc.append("</lock>");
491 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800492 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700493 String lockReply = sendRequest(rpc.toString());
494 return checkReply(lockReply);
andreaeb70a942015-10-16 21:34:46 -0700495 }
496
497 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700498 public boolean unlock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800499 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700500 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700501 rpc.append("<unlock>");
502 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700503 rpc.append("<");
504 rpc.append(configType);
505 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700506 rpc.append("</target>");
507 rpc.append("</unlock>");
508 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800509 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700510 String unlockReply = sendRequest(rpc.toString());
511 return checkReply(unlockReply);
512 }
513
514 @Override
515 public boolean lock() throws NetconfException {
516 return lock("running");
517 }
518
519 @Override
520 public boolean unlock() throws NetconfException {
521 return unlock("running");
andreaeb70a942015-10-16 21:34:46 -0700522 }
523
524 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800525 public boolean close() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700526 return close(false);
527 }
528
Andrea Campanella101417d2015-12-11 17:58:07 -0800529 private boolean close(boolean force) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700530 StringBuilder rpc = new StringBuilder();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700531 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
andreaeb70a942015-10-16 21:34:46 -0700532 if (force) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700533 rpc.append("<kill-session/>");
andreaeb70a942015-10-16 21:34:46 -0700534 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700535 rpc.append("<close-session/>");
andreaeb70a942015-10-16 21:34:46 -0700536 }
andreaeb70a942015-10-16 21:34:46 -0700537 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800538 rpc.append(ENDPATTERN);
539 return checkReply(sendRequest(rpc.toString())) || close(true);
andreaeb70a942015-10-16 21:34:46 -0700540 }
541
542 @Override
543 public String getSessionId() {
544 if (serverCapabilities.contains("<session-id>")) {
545 String[] outer = serverCapabilities.split("<session-id>");
546 Preconditions.checkArgument(outer.length != 1,
547 "Error in retrieving the session id");
548 String[] value = outer[1].split("</session-id>");
549 Preconditions.checkArgument(value.length != 1,
550 "Error in retrieving the session id");
551 return value[0];
552 } else {
553 return String.valueOf(-1);
554 }
555 }
556
557 @Override
558 public String getServerCapabilities() {
559 return serverCapabilities;
560 }
561
562 @Override
563 public void setDeviceCapabilities(List<String> capabilities) {
564 deviceCapabilities = capabilities;
565 }
566
Andrea Campanella101417d2015-12-11 17:58:07 -0800567 @Override
568 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700569 streamHandler.addDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800570 }
571
572 @Override
573 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700574 streamHandler.removeDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800575 }
576
577 private boolean checkReply(String reply) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700578 if (reply != null) {
579 if (!reply.contains("<rpc-error>")) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900580 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700581 return true;
582 } else if (reply.contains("<ok/>")
583 || (reply.contains("<rpc-error>")
584 && reply.contains("warning"))) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900585 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700586 return true;
587 }
588 }
Andrea Campanellad264b492016-03-01 09:46:06 -0800589 log.warn("Device {} has error in reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700590 return false;
591 }
592
Andrea Campanella101417d2015-12-11 17:58:07 -0800593 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
andreaeb70a942015-10-16 21:34:46 -0700594
Andrea Campanella101417d2015-12-11 17:58:07 -0800595 @Override
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200596 public void notify(NetconfDeviceOutputEvent event) {
597 Optional<Integer> messageId = event.getMessageID();
helenyrwu0407c642016-06-09 12:01:30 -0700598
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200599 if (!messageId.isPresent()) {
600 errorReplies.add(event.getMessagePayload());
Andrea Campanellad264b492016-03-01 09:46:06 -0800601 log.error("Device {} sent error reply {}",
602 event.getDeviceInfo(), event.getMessagePayload());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200603 return;
604 }
605 CompletableFuture<String> completedReply =
606 replies.get(messageId.get());
Akihiro Yamanouchi45122222016-07-15 13:13:11 +0900607 if (completedReply != null) {
608 completedReply.complete(event.getMessagePayload());
609 }
andreaeb70a942015-10-16 21:34:46 -0700610 }
611 }
andreaeb70a942015-10-16 21:34:46 -0700612}