blob: e35964165ee4a9a13ae67637de1273db01fb63ca [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
19import ch.ethz.ssh2.Connection;
20import ch.ethz.ssh2.Session;
21import com.google.common.base.Preconditions;
22import org.onosproject.netconf.NetconfDeviceInfo;
Andrea Campanella101417d2015-12-11 17:58:07 -080023import org.onosproject.netconf.NetconfDeviceOutputEvent;
24import org.onosproject.netconf.NetconfDeviceOutputEventListener;
25import org.onosproject.netconf.NetconfException;
andreaeb70a942015-10-16 21:34:46 -070026import org.onosproject.netconf.NetconfSession;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
andreaeb70a942015-10-16 21:34:46 -070030import java.io.IOException;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020031import java.util.ArrayList;
Andrea Campanella1cd641b2015-12-07 17:28:34 -080032import java.util.Collections;
Andrea Campanella101417d2015-12-11 17:58:07 -080033import java.util.HashMap;
andreaeb70a942015-10-16 21:34:46 -070034import java.util.List;
Andrea Campanella101417d2015-12-11 17:58:07 -080035import java.util.Map;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020036import java.util.Optional;
Andrea Campanella101417d2015-12-11 17:58:07 -080037import java.util.concurrent.CompletableFuture;
Andrea Campanellab029b9e2016-01-29 11:05:36 -080038import java.util.concurrent.ExecutionException;
39import java.util.concurrent.TimeUnit;
40import java.util.concurrent.TimeoutException;
Andrea Campanella101417d2015-12-11 17:58:07 -080041import java.util.concurrent.atomic.AtomicInteger;
42
andreaeb70a942015-10-16 21:34:46 -070043
44/**
45 * Implementation of a NETCONF session to talk to a device.
46 */
47public class NetconfSessionImpl implements NetconfSession {
48
Andrea Campanella101417d2015-12-11 17:58:07 -080049 private static final Logger log = LoggerFactory
andreaeb70a942015-10-16 21:34:46 -070050 .getLogger(NetconfSessionImpl.class);
Andrea Campanella101417d2015-12-11 17:58:07 -080051
52
andreaeb70a942015-10-16 21:34:46 -070053 private static final int CONNECTION_TIMEOUT = 0;
Andrea Campanella101417d2015-12-11 17:58:07 -080054 private static final String ENDPATTERN = "]]>]]>";
Andrea Campanella101417d2015-12-11 17:58:07 -080055 private static final String MESSAGE_ID_STRING = "message-id";
Andrea Campanella1311ea02016-03-04 17:51:25 -080056 private static final String HELLO = "<hello";
Andrea Campanella101417d2015-12-11 17:58:07 -080057 private static final String NEW_LINE = "\n";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080058 private static final String END_OF_RPC_OPEN_TAG = "\">";
59 private static final String EQUAL = "=";
60 private static final String NUMBER_BETWEEN_QUOTES_MATCHER = "\"+([0-9]+)+\"";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090061 private static final String RPC_OPEN = "<rpc ";
62 private static final String RPC_CLOSE = "</rpc>";
63 private static final String GET_OPEN = "<get>";
64 private static final String GET_CLOSE = "</get>";
65 private static final String WITH_DEFAULT_OPEN = "<with-defaults ";
66 private static final String WITH_DEFAULT_CLOSE = "</with-defaults>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090067 private static final String DEFAULT_OPERATION_OPEN = "<default-operation>";
68 private static final String DEFAULT_OPERATION_CLOSE = "</default-operation>";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090069 private static final String FILTER_OPEN = "<filter type=\"subtree\">";
70 private static final String FILTER_CLOSE = "</filter>";
Akihiro Yamanouchid4912842016-07-01 10:38:46 +090071 private static final String EDIT_CONFIG_OPEN = "<edit-config>";
72 private static final String EDIT_CONFIG_CLOSE = "</edit-config>";
73 private static final String TARGET_OPEN = "<target>";
74 private static final String TARGET_CLOSE = "</target>";
75 private static final String CONFIG_OPEN = "<config>";
76 private static final String CONFIG_CLOSE = "</config>";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080077 private static final String XML_HEADER =
78 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090079 private static final String NETCONF_BASE_NAMESPACE =
80 "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"";
81 private static final String NETCONF_WITH_DEFAULTS_NAMESPACE =
82 "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"";
andreaeb70a942015-10-16 21:34:46 -070083
Andrea Campanellab029b9e2016-01-29 11:05:36 -080084 private final AtomicInteger messageIdInteger = new AtomicInteger(0);
andreaeb70a942015-10-16 21:34:46 -070085 private Connection netconfConnection;
86 private NetconfDeviceInfo deviceInfo;
87 private Session sshSession;
88 private boolean connectionActive;
andreaeb70a942015-10-16 21:34:46 -070089 private List<String> deviceCapabilities =
Andrea Campanella1cd641b2015-12-07 17:28:34 -080090 Collections.singletonList("urn:ietf:params:netconf:base:1.0");
andreaeb70a942015-10-16 21:34:46 -070091 private String serverCapabilities;
helenyrwu0407c642016-06-09 12:01:30 -070092 private NetconfStreamHandler streamHandler;
Andrea Campanella101417d2015-12-11 17:58:07 -080093 private Map<Integer, CompletableFuture<String>> replies;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020094 private List<String> errorReplies;
helenyrwu0407c642016-06-09 12:01:30 -070095 private boolean subscriptionConnected = false;
andreaeb70a942015-10-16 21:34:46 -070096
97
Andrea Campanella101417d2015-12-11 17:58:07 -080098 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -070099 this.deviceInfo = deviceInfo;
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900100 this.netconfConnection = null;
101 this.sshSession = null;
andreaeb70a942015-10-16 21:34:46 -0700102 connectionActive = false;
Andrea Campanella101417d2015-12-11 17:58:07 -0800103 replies = new HashMap<>();
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200104 errorReplies = new ArrayList<>();
andreaeb70a942015-10-16 21:34:46 -0700105 startConnection();
106 }
107
Andrea Campanella101417d2015-12-11 17:58:07 -0800108 private void startConnection() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700109 if (!connectionActive) {
110 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
Andrea Campanella101417d2015-12-11 17:58:07 -0800111 try {
112 netconfConnection.connect(null, CONNECTION_TIMEOUT, 5000);
113 } catch (IOException e) {
114 throw new NetconfException("Cannot open a connection with device" + deviceInfo, e);
115 }
andreaeb70a942015-10-16 21:34:46 -0700116 boolean isAuthenticated;
117 try {
118 if (deviceInfo.getKeyFile() != null) {
119 isAuthenticated = netconfConnection.authenticateWithPublicKey(
120 deviceInfo.name(), deviceInfo.getKeyFile(),
121 deviceInfo.password());
122 } else {
Andrea Campanella101417d2015-12-11 17:58:07 -0800123 log.debug("Authenticating to device {} with username {}",
Andrea Campanella50d25212016-02-26 13:06:23 -0800124 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700125 isAuthenticated = netconfConnection.authenticateWithPassword(
126 deviceInfo.name(), deviceInfo.password());
127 }
128 } catch (IOException e) {
Andrea Campanellad264b492016-03-01 09:46:06 -0800129 log.error("Authentication connection to device {} failed: {} ",
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900130 deviceInfo.getDeviceId(), e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800131 throw new NetconfException("Authentication connection to device " +
132 deviceInfo.getDeviceId() + " failed", e);
andreaeb70a942015-10-16 21:34:46 -0700133 }
134
135 connectionActive = true;
136 Preconditions.checkArgument(isAuthenticated,
Andrea Campanellad264b492016-03-01 09:46:06 -0800137 "Authentication to device %s with username " +
138 "%s failed",
Andrea Campanella50d25212016-02-26 13:06:23 -0800139 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700140 startSshSession();
141 }
142 }
143
Andrea Campanella101417d2015-12-11 17:58:07 -0800144 private void startSshSession() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700145 try {
146 sshSession = netconfConnection.openSession();
147 sshSession.startSubSystem("netconf");
helenyrwu0407c642016-06-09 12:01:30 -0700148 streamHandler = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
149 sshSession.getStderr(), deviceInfo,
150 new NetconfSessionDelegateImpl());
Andrea Campanella101417d2015-12-11 17:58:07 -0800151 this.addDeviceOutputListener(new NetconfDeviceOutputEventListenerImpl(deviceInfo));
andreaeb70a942015-10-16 21:34:46 -0700152 sendHello();
153 } catch (IOException e) {
Andrea Campanella1311ea02016-03-04 17:51:25 -0800154 log.error("Failed to create ch.ethz.ssh2.Session session." + e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800155 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
156 deviceInfo, e);
andreaeb70a942015-10-16 21:34:46 -0700157 }
158 }
159
helenyrwu0407c642016-06-09 12:01:30 -0700160 private void startSubscriptionConnection() throws NetconfException {
161 if (!serverCapabilities.contains("interleave")) {
162 throw new NetconfException("Device" + deviceInfo + "does not support interleave");
163 }
164 String reply = sendRequest(createSubscriptionString());
165 if (!checkReply(reply)) {
166 throw new NetconfException("Subscription not successful with device "
167 + deviceInfo + " with reply " + reply);
168 }
169 subscriptionConnected = true;
170 }
171
172 public void startSubscription() throws NetconfException {
173 if (!subscriptionConnected) {
174 startSubscriptionConnection();
175 }
176 streamHandler.setEnableNotifications(true);
177 }
178
179 private String createSubscriptionString() {
180 StringBuilder subscriptionbuffer = new StringBuilder();
181 subscriptionbuffer.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
182 subscriptionbuffer.append(" <create-subscription\n");
183 subscriptionbuffer.append("xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n");
184 subscriptionbuffer.append(" </create-subscription>\n");
185 subscriptionbuffer.append("</rpc>\n");
186 subscriptionbuffer.append(ENDPATTERN);
187 return subscriptionbuffer.toString();
188 }
189
190 @Override
191 public void endSubscription() throws NetconfException {
192 if (subscriptionConnected) {
193 streamHandler.setEnableNotifications(false);
194 } else {
195 throw new NetconfException("Subscription does not exist.");
196 }
197 }
198
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800199 private void sendHello() throws NetconfException {
Andrea Campanella101417d2015-12-11 17:58:07 -0800200 serverCapabilities = sendRequest(createHelloString());
andreaeb70a942015-10-16 21:34:46 -0700201 }
202
203 private String createHelloString() {
204 StringBuilder hellobuffer = new StringBuilder();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800205 hellobuffer.append(XML_HEADER);
206 hellobuffer.append("\n");
andreaeb70a942015-10-16 21:34:46 -0700207 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
208 hellobuffer.append(" <capabilities>\n");
209 deviceCapabilities.forEach(
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800210 cap -> hellobuffer.append(" <capability>")
211 .append(cap)
212 .append("</capability>\n"));
andreaeb70a942015-10-16 21:34:46 -0700213 hellobuffer.append(" </capabilities>\n");
214 hellobuffer.append("</hello>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800215 hellobuffer.append(ENDPATTERN);
andreaeb70a942015-10-16 21:34:46 -0700216 return hellobuffer.toString();
217
218 }
219
Andrea Campanella101417d2015-12-11 17:58:07 -0800220 private void checkAndRestablishSession() throws NetconfException {
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800221 if (sshSession.getState() != 2) {
222 try {
223 startSshSession();
224 } catch (IOException e) {
Andrea Campanella101417d2015-12-11 17:58:07 -0800225 log.debug("The connection with {} had to be reopened", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800226 try {
227 startConnection();
228 } catch (IOException e2) {
Andrea Campanella50d25212016-02-26 13:06:23 -0800229 log.error("No connection {} for device", netconfConnection, e2);
Andrea Campanella101417d2015-12-11 17:58:07 -0800230 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800231 }
232 }
233 }
234 }
235
andreaeb70a942015-10-16 21:34:46 -0700236 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800237 public String requestSync(String request) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800238 if (!request.contains(ENDPATTERN)) {
239 request = request + NEW_LINE + ENDPATTERN;
240 }
241 String reply = sendRequest(request);
Andreas Papazois2e557be2016-06-04 15:39:56 +0300242 checkReply(reply);
243 return reply;
andreaeb70a942015-10-16 21:34:46 -0700244 }
245
246 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800247 public CompletableFuture<String> request(String request) {
helenyrwu0407c642016-06-09 12:01:30 -0700248 CompletableFuture<String> ftrep = streamHandler.sendMessage(request);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800249 replies.put(messageIdInteger.get(), ftrep);
Andrea Campanella101417d2015-12-11 17:58:07 -0800250 return ftrep;
251 }
252
253 private String sendRequest(String request) throws NetconfException {
254 checkAndRestablishSession();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800255 request = formatRequestMessageId(request);
256 request = formatXmlHeader(request);
Andrea Campanella101417d2015-12-11 17:58:07 -0800257 CompletableFuture<String> futureReply = request(request);
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200258 messageIdInteger.incrementAndGet();
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300259 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800260 String rp;
261 try {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300262 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800263 } catch (InterruptedException | ExecutionException | TimeoutException e) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200264 throw new NetconfException("No matching reply for request " + request, e);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800265 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800266 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
Andrea Campanella50d25212016-02-26 13:06:23 -0800267 return rp.trim();
Andrea Campanella101417d2015-12-11 17:58:07 -0800268 }
269
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800270 private String formatRequestMessageId(String request) {
271 if (request.contains(MESSAGE_ID_STRING)) {
272 //FIXME if application provieds his own counting of messages this fails that count
273 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
274 MESSAGE_ID_STRING + EQUAL + "\"" + messageIdInteger.get() + "\"");
275 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
276 //FIXME find out a better way to enforce the presence of message-id
277 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
278 + messageIdInteger.get() + "\"" + ">");
279 }
280 return request;
281 }
282
283 private String formatXmlHeader(String request) {
284 if (!request.contains(XML_HEADER)) {
285 //FIXME if application provieds his own XML header of different type there is a clash
286 request = XML_HEADER + "\n" + request;
287 }
288 return request;
289 }
290
Andrea Campanella101417d2015-12-11 17:58:07 -0800291 @Override
Akihiro Yamanouchi8d3a9d32016-07-12 11:41:44 +0900292 public String doWrappedRpc(String request) throws NetconfException {
293 StringBuilder rpc = new StringBuilder(XML_HEADER);
294 rpc.append(RPC_OPEN);
295 rpc.append(MESSAGE_ID_STRING);
296 rpc.append(EQUAL);
297 rpc.append("\"");
298 rpc.append(messageIdInteger.get());
299 rpc.append("\" ");
300 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
301 rpc.append(request);
302 rpc.append(RPC_CLOSE).append(NEW_LINE);
303 rpc.append(ENDPATTERN);
304 String reply = sendRequest(rpc.toString());
305 checkReply(reply);
306 return reply;
307 }
308
309 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800310 public String get(String request) throws NetconfException {
311 return requestSync(request);
312 }
313
314 @Override
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900315 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
316 StringBuilder rpc = new StringBuilder(XML_HEADER);
317 rpc.append(RPC_OPEN);
318 rpc.append(MESSAGE_ID_STRING);
319 rpc.append(EQUAL);
320 rpc.append("\"");
321 rpc.append(messageIdInteger.get());
322 rpc.append("\" ");
323 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
324 rpc.append(GET_OPEN).append(NEW_LINE);
325 if (filterSchema != null) {
326 rpc.append(FILTER_OPEN).append(NEW_LINE);
327 rpc.append(filterSchema).append(NEW_LINE);
328 rpc.append(FILTER_CLOSE).append(NEW_LINE);
329 }
330 if (withDefaultsMode != null) {
331 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
332 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
333 }
334 rpc.append(GET_CLOSE).append(NEW_LINE);
335 rpc.append(RPC_CLOSE).append(NEW_LINE);
336 rpc.append(ENDPATTERN);
337 String reply = sendRequest(rpc.toString());
338 checkReply(reply);
339 return reply;
340 }
341
342 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800343 public String getConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700344 return getConfig(targetConfiguration, null);
345 }
346
347 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800348 public String getConfig(String targetConfiguration, String configurationSchema) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800349 StringBuilder rpc = new StringBuilder(XML_HEADER);
350 rpc.append("<rpc ");
351 rpc.append(MESSAGE_ID_STRING);
352 rpc.append(EQUAL);
353 rpc.append("\"");
354 rpc.append(messageIdInteger.get());
355 rpc.append("\" ");
356 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700357 rpc.append("<get-config>\n");
358 rpc.append("<source>\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800359 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700360 rpc.append("</source>");
361 if (configurationSchema != null) {
362 rpc.append("<filter type=\"subtree\">\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800363 rpc.append(configurationSchema).append("\n");
andreaeb70a942015-10-16 21:34:46 -0700364 rpc.append("</filter>\n");
365 }
366 rpc.append("</get-config>\n");
367 rpc.append("</rpc>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800368 rpc.append(ENDPATTERN);
369 String reply = sendRequest(rpc.toString());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800370 return checkReply(reply) ? reply : "ERROR " + reply;
andreaeb70a942015-10-16 21:34:46 -0700371 }
372
373 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800374 public boolean editConfig(String newConfiguration) throws NetconfException {
375 newConfiguration = newConfiguration + ENDPATTERN;
376 return checkReply(sendRequest(newConfiguration));
andreaeb70a942015-10-16 21:34:46 -0700377 }
378
379 @Override
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800380 public boolean editConfig(String targetConfiguration, String mode, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800381 throws NetconfException {
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800382 newConfiguration = newConfiguration.trim();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800383 StringBuilder rpc = new StringBuilder(XML_HEADER);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900384 rpc.append(RPC_OPEN);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800385 rpc.append(MESSAGE_ID_STRING);
386 rpc.append(EQUAL);
387 rpc.append("\"");
388 rpc.append(messageIdInteger.get());
389 rpc.append("\" ");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900390 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
391 rpc.append(EDIT_CONFIG_OPEN).append("\n");
392 rpc.append(TARGET_OPEN);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800393 rpc.append("<").append(targetConfiguration).append("/>");
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900394 rpc.append(TARGET_CLOSE).append("\n");
395 if (mode != null) {
396 rpc.append(DEFAULT_OPERATION_OPEN);
397 rpc.append(mode);
398 rpc.append(DEFAULT_OPERATION_CLOSE).append("\n");
399 }
400 rpc.append(CONFIG_OPEN).append("\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800401 rpc.append(newConfiguration);
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900402 rpc.append(CONFIG_CLOSE).append("\n");
403 rpc.append(EDIT_CONFIG_CLOSE).append("\n");
404 rpc.append(RPC_CLOSE);
Andrea Campanella101417d2015-12-11 17:58:07 -0800405 rpc.append(ENDPATTERN);
Andrea Campanella50d25212016-02-26 13:06:23 -0800406 log.info(rpc.toString());
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900407 String reply = sendRequest(rpc.toString());
408 return checkReply(reply);
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800409 }
410
411 @Override
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800412 public boolean copyConfig(String targetConfiguration, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800413 throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700414 newConfiguration = newConfiguration.trim();
415 if (!newConfiguration.startsWith("<configuration>")) {
416 newConfiguration = "<configuration>" + newConfiguration
417 + "</configuration>";
418 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800419 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700420 rpc.append("<rpc>");
421 rpc.append("<copy-config>");
422 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800423 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700424 rpc.append("</target>");
425 rpc.append("<source>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800426 rpc.append("<").append(newConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700427 rpc.append("</source>");
428 rpc.append("</copy-config>");
429 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800430 rpc.append(ENDPATTERN);
431 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700432 }
433
434 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800435 public boolean deleteConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700436 if (targetConfiguration.equals("running")) {
437 log.warn("Target configuration for delete operation can't be \"running\"",
438 targetConfiguration);
439 return false;
440 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800441 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700442 rpc.append("<rpc>");
443 rpc.append("<delete-config>");
444 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800445 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700446 rpc.append("</target>");
447 rpc.append("</delete-config>");
448 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800449 rpc.append(ENDPATTERN);
450 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700451 }
452
453 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700454 public boolean lock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800455 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700456 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700457 rpc.append("<lock>");
458 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700459 rpc.append("<");
460 rpc.append(configType);
461 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700462 rpc.append("</target>");
463 rpc.append("</lock>");
464 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800465 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700466 String lockReply = sendRequest(rpc.toString());
467 return checkReply(lockReply);
andreaeb70a942015-10-16 21:34:46 -0700468 }
469
470 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700471 public boolean unlock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800472 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700473 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700474 rpc.append("<unlock>");
475 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700476 rpc.append("<");
477 rpc.append(configType);
478 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700479 rpc.append("</target>");
480 rpc.append("</unlock>");
481 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800482 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700483 String unlockReply = sendRequest(rpc.toString());
484 return checkReply(unlockReply);
485 }
486
487 @Override
488 public boolean lock() throws NetconfException {
489 return lock("running");
490 }
491
492 @Override
493 public boolean unlock() throws NetconfException {
494 return unlock("running");
andreaeb70a942015-10-16 21:34:46 -0700495 }
496
497 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800498 public boolean close() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700499 return close(false);
500 }
501
Andrea Campanella101417d2015-12-11 17:58:07 -0800502 private boolean close(boolean force) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700503 StringBuilder rpc = new StringBuilder();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700504 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
andreaeb70a942015-10-16 21:34:46 -0700505 if (force) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700506 rpc.append("<kill-session/>");
andreaeb70a942015-10-16 21:34:46 -0700507 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700508 rpc.append("<close-session/>");
andreaeb70a942015-10-16 21:34:46 -0700509 }
andreaeb70a942015-10-16 21:34:46 -0700510 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800511 rpc.append(ENDPATTERN);
512 return checkReply(sendRequest(rpc.toString())) || close(true);
andreaeb70a942015-10-16 21:34:46 -0700513 }
514
515 @Override
516 public String getSessionId() {
517 if (serverCapabilities.contains("<session-id>")) {
518 String[] outer = serverCapabilities.split("<session-id>");
519 Preconditions.checkArgument(outer.length != 1,
520 "Error in retrieving the session id");
521 String[] value = outer[1].split("</session-id>");
522 Preconditions.checkArgument(value.length != 1,
523 "Error in retrieving the session id");
524 return value[0];
525 } else {
526 return String.valueOf(-1);
527 }
528 }
529
530 @Override
531 public String getServerCapabilities() {
532 return serverCapabilities;
533 }
534
535 @Override
536 public void setDeviceCapabilities(List<String> capabilities) {
537 deviceCapabilities = capabilities;
538 }
539
Andrea Campanella101417d2015-12-11 17:58:07 -0800540 @Override
541 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700542 streamHandler.addDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800543 }
544
545 @Override
546 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700547 streamHandler.removeDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800548 }
549
550 private boolean checkReply(String reply) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700551 if (reply != null) {
552 if (!reply.contains("<rpc-error>")) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900553 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700554 return true;
555 } else if (reply.contains("<ok/>")
556 || (reply.contains("<rpc-error>")
557 && reply.contains("warning"))) {
Akihiro Yamanouchid4912842016-07-01 10:38:46 +0900558 log.debug("Device {} sent reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700559 return true;
560 }
561 }
Andrea Campanellad264b492016-03-01 09:46:06 -0800562 log.warn("Device {} has error in reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700563 return false;
564 }
565
Andrea Campanella101417d2015-12-11 17:58:07 -0800566 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
andreaeb70a942015-10-16 21:34:46 -0700567
Andrea Campanella101417d2015-12-11 17:58:07 -0800568 @Override
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200569 public void notify(NetconfDeviceOutputEvent event) {
570 Optional<Integer> messageId = event.getMessageID();
helenyrwu0407c642016-06-09 12:01:30 -0700571
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200572 if (!messageId.isPresent()) {
573 errorReplies.add(event.getMessagePayload());
Andrea Campanellad264b492016-03-01 09:46:06 -0800574 log.error("Device {} sent error reply {}",
575 event.getDeviceInfo(), event.getMessagePayload());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200576 return;
577 }
578 CompletableFuture<String> completedReply =
579 replies.get(messageId.get());
Andrea Campanella101417d2015-12-11 17:58:07 -0800580 completedReply.complete(event.getMessagePayload());
andreaeb70a942015-10-16 21:34:46 -0700581 }
582 }
andreaeb70a942015-10-16 21:34:46 -0700583}