blob: 487f4cc551daaea7a13c4de97fef7a8a15558404 [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>";
67 private static final String FILTER_OPEN = "<filter type=\"subtree\">";
68 private static final String FILTER_CLOSE = "</filter>";
Andrea Campanellab029b9e2016-01-29 11:05:36 -080069 private static final String XML_HEADER =
70 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090071 private static final String NETCONF_BASE_NAMESPACE =
72 "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"";
73 private static final String NETCONF_WITH_DEFAULTS_NAMESPACE =
74 "xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"";
andreaeb70a942015-10-16 21:34:46 -070075
Andrea Campanellab029b9e2016-01-29 11:05:36 -080076 private final AtomicInteger messageIdInteger = new AtomicInteger(0);
andreaeb70a942015-10-16 21:34:46 -070077 private Connection netconfConnection;
78 private NetconfDeviceInfo deviceInfo;
79 private Session sshSession;
80 private boolean connectionActive;
andreaeb70a942015-10-16 21:34:46 -070081 private List<String> deviceCapabilities =
Andrea Campanella1cd641b2015-12-07 17:28:34 -080082 Collections.singletonList("urn:ietf:params:netconf:base:1.0");
andreaeb70a942015-10-16 21:34:46 -070083 private String serverCapabilities;
Andrea Campanella101417d2015-12-11 17:58:07 -080084 private NetconfStreamHandler t;
85 private Map<Integer, CompletableFuture<String>> replies;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020086 private List<String> errorReplies;
andreaeb70a942015-10-16 21:34:46 -070087
88
Andrea Campanella101417d2015-12-11 17:58:07 -080089 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -070090 this.deviceInfo = deviceInfo;
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090091 this.netconfConnection = null;
92 this.sshSession = null;
andreaeb70a942015-10-16 21:34:46 -070093 connectionActive = false;
Andrea Campanella101417d2015-12-11 17:58:07 -080094 replies = new HashMap<>();
Andreas Papazoisd4712e22016-02-10 15:59:55 +020095 errorReplies = new ArrayList<>();
andreaeb70a942015-10-16 21:34:46 -070096 startConnection();
97 }
98
Andrea Campanella101417d2015-12-11 17:58:07 -080099 private void startConnection() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700100 if (!connectionActive) {
101 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
Andrea Campanella101417d2015-12-11 17:58:07 -0800102 try {
103 netconfConnection.connect(null, CONNECTION_TIMEOUT, 5000);
104 } catch (IOException e) {
105 throw new NetconfException("Cannot open a connection with device" + deviceInfo, e);
106 }
andreaeb70a942015-10-16 21:34:46 -0700107 boolean isAuthenticated;
108 try {
109 if (deviceInfo.getKeyFile() != null) {
110 isAuthenticated = netconfConnection.authenticateWithPublicKey(
111 deviceInfo.name(), deviceInfo.getKeyFile(),
112 deviceInfo.password());
113 } else {
Andrea Campanella101417d2015-12-11 17:58:07 -0800114 log.debug("Authenticating to device {} with username {}",
Andrea Campanella50d25212016-02-26 13:06:23 -0800115 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700116 isAuthenticated = netconfConnection.authenticateWithPassword(
117 deviceInfo.name(), deviceInfo.password());
118 }
119 } catch (IOException e) {
Andrea Campanellad264b492016-03-01 09:46:06 -0800120 log.error("Authentication connection to device {} failed: {} ",
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900121 deviceInfo.getDeviceId(), e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800122 throw new NetconfException("Authentication connection to device " +
123 deviceInfo.getDeviceId() + " failed", e);
andreaeb70a942015-10-16 21:34:46 -0700124 }
125
126 connectionActive = true;
127 Preconditions.checkArgument(isAuthenticated,
Andrea Campanellad264b492016-03-01 09:46:06 -0800128 "Authentication to device %s with username " +
129 "%s failed",
Andrea Campanella50d25212016-02-26 13:06:23 -0800130 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700131 startSshSession();
132 }
133 }
134
Andrea Campanella101417d2015-12-11 17:58:07 -0800135 private void startSshSession() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700136 try {
137 sshSession = netconfConnection.openSession();
138 sshSession.startSubSystem("netconf");
Andrea Campanella101417d2015-12-11 17:58:07 -0800139 t = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
140 sshSession.getStderr(), deviceInfo,
141 new NetconfSessionDelegateImpl());
142 this.addDeviceOutputListener(new NetconfDeviceOutputEventListenerImpl(deviceInfo));
andreaeb70a942015-10-16 21:34:46 -0700143 sendHello();
144 } catch (IOException e) {
Andrea Campanella1311ea02016-03-04 17:51:25 -0800145 log.error("Failed to create ch.ethz.ssh2.Session session." + e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800146 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
147 deviceInfo, e);
andreaeb70a942015-10-16 21:34:46 -0700148 }
149 }
150
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800151 private void sendHello() throws NetconfException {
Andrea Campanella101417d2015-12-11 17:58:07 -0800152 serverCapabilities = sendRequest(createHelloString());
andreaeb70a942015-10-16 21:34:46 -0700153 }
154
155 private String createHelloString() {
156 StringBuilder hellobuffer = new StringBuilder();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800157 hellobuffer.append(XML_HEADER);
158 hellobuffer.append("\n");
andreaeb70a942015-10-16 21:34:46 -0700159 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
160 hellobuffer.append(" <capabilities>\n");
161 deviceCapabilities.forEach(
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800162 cap -> hellobuffer.append(" <capability>")
163 .append(cap)
164 .append("</capability>\n"));
andreaeb70a942015-10-16 21:34:46 -0700165 hellobuffer.append(" </capabilities>\n");
166 hellobuffer.append("</hello>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800167 hellobuffer.append(ENDPATTERN);
andreaeb70a942015-10-16 21:34:46 -0700168 return hellobuffer.toString();
169
170 }
171
Andrea Campanella101417d2015-12-11 17:58:07 -0800172 private void checkAndRestablishSession() throws NetconfException {
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800173 if (sshSession.getState() != 2) {
174 try {
175 startSshSession();
176 } catch (IOException e) {
Andrea Campanella101417d2015-12-11 17:58:07 -0800177 log.debug("The connection with {} had to be reopened", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800178 try {
179 startConnection();
180 } catch (IOException e2) {
Andrea Campanella50d25212016-02-26 13:06:23 -0800181 log.error("No connection {} for device", netconfConnection, e2);
Andrea Campanella101417d2015-12-11 17:58:07 -0800182 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800183 }
184 }
185 }
186 }
187
andreaeb70a942015-10-16 21:34:46 -0700188 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800189 public String requestSync(String request) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800190 if (!request.contains(ENDPATTERN)) {
191 request = request + NEW_LINE + ENDPATTERN;
192 }
193 String reply = sendRequest(request);
Andreas Papazois2e557be2016-06-04 15:39:56 +0300194 checkReply(reply);
195 return reply;
andreaeb70a942015-10-16 21:34:46 -0700196 }
197
198 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800199 public CompletableFuture<String> request(String request) {
200 CompletableFuture<String> ftrep = t.sendMessage(request);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800201 replies.put(messageIdInteger.get(), ftrep);
Andrea Campanella101417d2015-12-11 17:58:07 -0800202 return ftrep;
203 }
204
205 private String sendRequest(String request) throws NetconfException {
206 checkAndRestablishSession();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800207 request = formatRequestMessageId(request);
208 request = formatXmlHeader(request);
Andrea Campanella101417d2015-12-11 17:58:07 -0800209 CompletableFuture<String> futureReply = request(request);
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200210 messageIdInteger.incrementAndGet();
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300211 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800212 String rp;
213 try {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300214 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800215 } catch (InterruptedException | ExecutionException | TimeoutException e) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200216 throw new NetconfException("No matching reply for request " + request, e);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800217 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800218 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
Andrea Campanella50d25212016-02-26 13:06:23 -0800219 return rp.trim();
Andrea Campanella101417d2015-12-11 17:58:07 -0800220 }
221
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800222 private String formatRequestMessageId(String request) {
223 if (request.contains(MESSAGE_ID_STRING)) {
224 //FIXME if application provieds his own counting of messages this fails that count
225 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
226 MESSAGE_ID_STRING + EQUAL + "\"" + messageIdInteger.get() + "\"");
227 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
228 //FIXME find out a better way to enforce the presence of message-id
229 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
230 + messageIdInteger.get() + "\"" + ">");
231 }
232 return request;
233 }
234
235 private String formatXmlHeader(String request) {
236 if (!request.contains(XML_HEADER)) {
237 //FIXME if application provieds his own XML header of different type there is a clash
238 request = XML_HEADER + "\n" + request;
239 }
240 return request;
241 }
242
Andrea Campanella101417d2015-12-11 17:58:07 -0800243 @Override
244 public String get(String request) throws NetconfException {
245 return requestSync(request);
246 }
247
248 @Override
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900249 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
250 StringBuilder rpc = new StringBuilder(XML_HEADER);
251 rpc.append(RPC_OPEN);
252 rpc.append(MESSAGE_ID_STRING);
253 rpc.append(EQUAL);
254 rpc.append("\"");
255 rpc.append(messageIdInteger.get());
256 rpc.append("\" ");
257 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
258 rpc.append(GET_OPEN).append(NEW_LINE);
259 if (filterSchema != null) {
260 rpc.append(FILTER_OPEN).append(NEW_LINE);
261 rpc.append(filterSchema).append(NEW_LINE);
262 rpc.append(FILTER_CLOSE).append(NEW_LINE);
263 }
264 if (withDefaultsMode != null) {
265 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
266 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
267 }
268 rpc.append(GET_CLOSE).append(NEW_LINE);
269 rpc.append(RPC_CLOSE).append(NEW_LINE);
270 rpc.append(ENDPATTERN);
271 String reply = sendRequest(rpc.toString());
272 checkReply(reply);
273 return reply;
274 }
275
276 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800277 public String getConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700278 return getConfig(targetConfiguration, null);
279 }
280
281 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800282 public String getConfig(String targetConfiguration, String configurationSchema) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800283 StringBuilder rpc = new StringBuilder(XML_HEADER);
284 rpc.append("<rpc ");
285 rpc.append(MESSAGE_ID_STRING);
286 rpc.append(EQUAL);
287 rpc.append("\"");
288 rpc.append(messageIdInteger.get());
289 rpc.append("\" ");
290 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700291 rpc.append("<get-config>\n");
292 rpc.append("<source>\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800293 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700294 rpc.append("</source>");
295 if (configurationSchema != null) {
296 rpc.append("<filter type=\"subtree\">\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800297 rpc.append(configurationSchema).append("\n");
andreaeb70a942015-10-16 21:34:46 -0700298 rpc.append("</filter>\n");
299 }
300 rpc.append("</get-config>\n");
301 rpc.append("</rpc>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800302 rpc.append(ENDPATTERN);
303 String reply = sendRequest(rpc.toString());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800304 return checkReply(reply) ? reply : "ERROR " + reply;
andreaeb70a942015-10-16 21:34:46 -0700305 }
306
307 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800308 public boolean editConfig(String newConfiguration) throws NetconfException {
309 newConfiguration = newConfiguration + ENDPATTERN;
310 return checkReply(sendRequest(newConfiguration));
andreaeb70a942015-10-16 21:34:46 -0700311 }
312
313 @Override
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800314 public boolean editConfig(String targetConfiguration, String mode, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800315 throws NetconfException {
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800316 newConfiguration = newConfiguration.trim();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800317 StringBuilder rpc = new StringBuilder(XML_HEADER);
318 rpc.append("<rpc ");
319 rpc.append(MESSAGE_ID_STRING);
320 rpc.append(EQUAL);
321 rpc.append("\"");
322 rpc.append(messageIdInteger.get());
323 rpc.append("\" ");
324 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
Andrea Campanella50d25212016-02-26 13:06:23 -0800325 rpc.append("<edit-config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800326 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800327 rpc.append("<").append(targetConfiguration).append("/>");
Andrea Campanella50d25212016-02-26 13:06:23 -0800328 rpc.append("</target>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800329 rpc.append("<default-operation>");
330 rpc.append(mode);
Andrea Campanella50d25212016-02-26 13:06:23 -0800331 rpc.append("</default-operation>\n");
332 rpc.append("<config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800333 rpc.append(newConfiguration);
Andrea Campanella50d25212016-02-26 13:06:23 -0800334 rpc.append("</config>\n");
335 rpc.append("</edit-config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800336 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800337 rpc.append(ENDPATTERN);
Andrea Campanella50d25212016-02-26 13:06:23 -0800338 log.info(rpc.toString());
Andrea Campanella101417d2015-12-11 17:58:07 -0800339 return checkReply(sendRequest(rpc.toString()));
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800340 }
341
342 @Override
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800343 public boolean copyConfig(String targetConfiguration, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800344 throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700345 newConfiguration = newConfiguration.trim();
346 if (!newConfiguration.startsWith("<configuration>")) {
347 newConfiguration = "<configuration>" + newConfiguration
348 + "</configuration>";
349 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800350 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700351 rpc.append("<rpc>");
352 rpc.append("<copy-config>");
353 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800354 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700355 rpc.append("</target>");
356 rpc.append("<source>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800357 rpc.append("<").append(newConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700358 rpc.append("</source>");
359 rpc.append("</copy-config>");
360 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800361 rpc.append(ENDPATTERN);
362 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700363 }
364
365 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800366 public boolean deleteConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700367 if (targetConfiguration.equals("running")) {
368 log.warn("Target configuration for delete operation can't be \"running\"",
369 targetConfiguration);
370 return false;
371 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800372 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700373 rpc.append("<rpc>");
374 rpc.append("<delete-config>");
375 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800376 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700377 rpc.append("</target>");
378 rpc.append("</delete-config>");
379 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800380 rpc.append(ENDPATTERN);
381 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700382 }
383
384 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800385 public boolean lock() throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800386 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700387 rpc.append("<rpc>");
388 rpc.append("<lock>");
389 rpc.append("<target>");
390 rpc.append("<candidate/>");
391 rpc.append("</target>");
392 rpc.append("</lock>");
393 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800394 rpc.append(ENDPATTERN);
395 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700396 }
397
398 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800399 public boolean unlock() throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800400 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700401 rpc.append("<rpc>");
402 rpc.append("<unlock>");
403 rpc.append("<target>");
404 rpc.append("<candidate/>");
405 rpc.append("</target>");
406 rpc.append("</unlock>");
407 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800408 rpc.append(ENDPATTERN);
409 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700410 }
411
412 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800413 public boolean close() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700414 return close(false);
415 }
416
Andrea Campanella101417d2015-12-11 17:58:07 -0800417 private boolean close(boolean force) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700418 StringBuilder rpc = new StringBuilder();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700419 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
andreaeb70a942015-10-16 21:34:46 -0700420 if (force) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700421 rpc.append("<kill-session/>");
andreaeb70a942015-10-16 21:34:46 -0700422 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700423 rpc.append("<close-session/>");
andreaeb70a942015-10-16 21:34:46 -0700424 }
andreaeb70a942015-10-16 21:34:46 -0700425 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800426 rpc.append(ENDPATTERN);
427 return checkReply(sendRequest(rpc.toString())) || close(true);
andreaeb70a942015-10-16 21:34:46 -0700428 }
429
430 @Override
431 public String getSessionId() {
432 if (serverCapabilities.contains("<session-id>")) {
433 String[] outer = serverCapabilities.split("<session-id>");
434 Preconditions.checkArgument(outer.length != 1,
435 "Error in retrieving the session id");
436 String[] value = outer[1].split("</session-id>");
437 Preconditions.checkArgument(value.length != 1,
438 "Error in retrieving the session id");
439 return value[0];
440 } else {
441 return String.valueOf(-1);
442 }
443 }
444
445 @Override
446 public String getServerCapabilities() {
447 return serverCapabilities;
448 }
449
450 @Override
451 public void setDeviceCapabilities(List<String> capabilities) {
452 deviceCapabilities = capabilities;
453 }
454
Andrea Campanella101417d2015-12-11 17:58:07 -0800455 @Override
456 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
457 t.addDeviceEventListener(listener);
458 }
459
460 @Override
461 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
462 t.removeDeviceEventListener(listener);
463 }
464
465 private boolean checkReply(String reply) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700466 if (reply != null) {
467 if (!reply.contains("<rpc-error>")) {
468 return true;
469 } else if (reply.contains("<ok/>")
470 || (reply.contains("<rpc-error>")
471 && reply.contains("warning"))) {
472 return true;
473 }
474 }
Andrea Campanellad264b492016-03-01 09:46:06 -0800475 log.warn("Device {} has error in reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700476 return false;
477 }
478
Andrea Campanella101417d2015-12-11 17:58:07 -0800479 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
andreaeb70a942015-10-16 21:34:46 -0700480
Andrea Campanella101417d2015-12-11 17:58:07 -0800481 @Override
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200482 public void notify(NetconfDeviceOutputEvent event) {
483 Optional<Integer> messageId = event.getMessageID();
484 if (!messageId.isPresent()) {
485 errorReplies.add(event.getMessagePayload());
Andrea Campanellad264b492016-03-01 09:46:06 -0800486 log.error("Device {} sent error reply {}",
487 event.getDeviceInfo(), event.getMessagePayload());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200488 return;
489 }
490 CompletableFuture<String> completedReply =
491 replies.get(messageId.get());
Andrea Campanella101417d2015-12-11 17:58:07 -0800492 completedReply.complete(event.getMessagePayload());
andreaeb70a942015-10-16 21:34:46 -0700493 }
494 }
andreaeb70a942015-10-16 21:34:46 -0700495}