blob: 1ee0f08fb698a542a830905328c7c70f9cee3650 [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;
helenyrwu0407c642016-06-09 12:01:30 -070084 private NetconfStreamHandler streamHandler;
Andrea Campanella101417d2015-12-11 17:58:07 -080085 private Map<Integer, CompletableFuture<String>> replies;
Andreas Papazoisd4712e22016-02-10 15:59:55 +020086 private List<String> errorReplies;
helenyrwu0407c642016-06-09 12:01:30 -070087 private boolean subscriptionConnected = false;
andreaeb70a942015-10-16 21:34:46 -070088
89
Andrea Campanella101417d2015-12-11 17:58:07 -080090 public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -070091 this.deviceInfo = deviceInfo;
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +090092 this.netconfConnection = null;
93 this.sshSession = null;
andreaeb70a942015-10-16 21:34:46 -070094 connectionActive = false;
Andrea Campanella101417d2015-12-11 17:58:07 -080095 replies = new HashMap<>();
Andreas Papazoisd4712e22016-02-10 15:59:55 +020096 errorReplies = new ArrayList<>();
andreaeb70a942015-10-16 21:34:46 -070097 startConnection();
98 }
99
Andrea Campanella101417d2015-12-11 17:58:07 -0800100 private void startConnection() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700101 if (!connectionActive) {
102 netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port());
Andrea Campanella101417d2015-12-11 17:58:07 -0800103 try {
104 netconfConnection.connect(null, CONNECTION_TIMEOUT, 5000);
105 } catch (IOException e) {
106 throw new NetconfException("Cannot open a connection with device" + deviceInfo, e);
107 }
andreaeb70a942015-10-16 21:34:46 -0700108 boolean isAuthenticated;
109 try {
110 if (deviceInfo.getKeyFile() != null) {
111 isAuthenticated = netconfConnection.authenticateWithPublicKey(
112 deviceInfo.name(), deviceInfo.getKeyFile(),
113 deviceInfo.password());
114 } else {
Andrea Campanella101417d2015-12-11 17:58:07 -0800115 log.debug("Authenticating to device {} with username {}",
Andrea Campanella50d25212016-02-26 13:06:23 -0800116 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700117 isAuthenticated = netconfConnection.authenticateWithPassword(
118 deviceInfo.name(), deviceInfo.password());
119 }
120 } catch (IOException e) {
Andrea Campanellad264b492016-03-01 09:46:06 -0800121 log.error("Authentication connection to device {} failed: {} ",
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900122 deviceInfo.getDeviceId(), e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800123 throw new NetconfException("Authentication connection to device " +
124 deviceInfo.getDeviceId() + " failed", e);
andreaeb70a942015-10-16 21:34:46 -0700125 }
126
127 connectionActive = true;
128 Preconditions.checkArgument(isAuthenticated,
Andrea Campanellad264b492016-03-01 09:46:06 -0800129 "Authentication to device %s with username " +
130 "%s failed",
Andrea Campanella50d25212016-02-26 13:06:23 -0800131 deviceInfo.getDeviceId(), deviceInfo.name());
andreaeb70a942015-10-16 21:34:46 -0700132 startSshSession();
133 }
134 }
135
Andrea Campanella101417d2015-12-11 17:58:07 -0800136 private void startSshSession() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700137 try {
138 sshSession = netconfConnection.openSession();
139 sshSession.startSubSystem("netconf");
helenyrwu0407c642016-06-09 12:01:30 -0700140 streamHandler = new NetconfStreamThread(sshSession.getStdout(), sshSession.getStdin(),
141 sshSession.getStderr(), deviceInfo,
142 new NetconfSessionDelegateImpl());
Andrea Campanella101417d2015-12-11 17:58:07 -0800143 this.addDeviceOutputListener(new NetconfDeviceOutputEventListenerImpl(deviceInfo));
andreaeb70a942015-10-16 21:34:46 -0700144 sendHello();
145 } catch (IOException e) {
Andrea Campanella1311ea02016-03-04 17:51:25 -0800146 log.error("Failed to create ch.ethz.ssh2.Session session." + e.getMessage());
Andrea Campanella101417d2015-12-11 17:58:07 -0800147 throw new NetconfException("Failed to create ch.ethz.ssh2.Session session with device" +
148 deviceInfo, e);
andreaeb70a942015-10-16 21:34:46 -0700149 }
150 }
151
helenyrwu0407c642016-06-09 12:01:30 -0700152 private void startSubscriptionConnection() throws NetconfException {
153 if (!serverCapabilities.contains("interleave")) {
154 throw new NetconfException("Device" + deviceInfo + "does not support interleave");
155 }
156 String reply = sendRequest(createSubscriptionString());
157 if (!checkReply(reply)) {
158 throw new NetconfException("Subscription not successful with device "
159 + deviceInfo + " with reply " + reply);
160 }
161 subscriptionConnected = true;
162 }
163
164 public void startSubscription() throws NetconfException {
165 if (!subscriptionConnected) {
166 startSubscriptionConnection();
167 }
168 streamHandler.setEnableNotifications(true);
169 }
170
171 private String createSubscriptionString() {
172 StringBuilder subscriptionbuffer = new StringBuilder();
173 subscriptionbuffer.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
174 subscriptionbuffer.append(" <create-subscription\n");
175 subscriptionbuffer.append("xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n");
176 subscriptionbuffer.append(" </create-subscription>\n");
177 subscriptionbuffer.append("</rpc>\n");
178 subscriptionbuffer.append(ENDPATTERN);
179 return subscriptionbuffer.toString();
180 }
181
182 @Override
183 public void endSubscription() throws NetconfException {
184 if (subscriptionConnected) {
185 streamHandler.setEnableNotifications(false);
186 } else {
187 throw new NetconfException("Subscription does not exist.");
188 }
189 }
190
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800191 private void sendHello() throws NetconfException {
Andrea Campanella101417d2015-12-11 17:58:07 -0800192 serverCapabilities = sendRequest(createHelloString());
andreaeb70a942015-10-16 21:34:46 -0700193 }
194
195 private String createHelloString() {
196 StringBuilder hellobuffer = new StringBuilder();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800197 hellobuffer.append(XML_HEADER);
198 hellobuffer.append("\n");
andreaeb70a942015-10-16 21:34:46 -0700199 hellobuffer.append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
200 hellobuffer.append(" <capabilities>\n");
201 deviceCapabilities.forEach(
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800202 cap -> hellobuffer.append(" <capability>")
203 .append(cap)
204 .append("</capability>\n"));
andreaeb70a942015-10-16 21:34:46 -0700205 hellobuffer.append(" </capabilities>\n");
206 hellobuffer.append("</hello>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800207 hellobuffer.append(ENDPATTERN);
andreaeb70a942015-10-16 21:34:46 -0700208 return hellobuffer.toString();
209
210 }
211
Andrea Campanella101417d2015-12-11 17:58:07 -0800212 private void checkAndRestablishSession() throws NetconfException {
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800213 if (sshSession.getState() != 2) {
214 try {
215 startSshSession();
216 } catch (IOException e) {
Andrea Campanella101417d2015-12-11 17:58:07 -0800217 log.debug("The connection with {} had to be reopened", deviceInfo.getDeviceId());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800218 try {
219 startConnection();
220 } catch (IOException e2) {
Andrea Campanella50d25212016-02-26 13:06:23 -0800221 log.error("No connection {} for device", netconfConnection, e2);
Andrea Campanella101417d2015-12-11 17:58:07 -0800222 throw new NetconfException("Cannot re-open the connection with device" + deviceInfo, e);
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800223 }
224 }
225 }
226 }
227
andreaeb70a942015-10-16 21:34:46 -0700228 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800229 public String requestSync(String request) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800230 if (!request.contains(ENDPATTERN)) {
231 request = request + NEW_LINE + ENDPATTERN;
232 }
233 String reply = sendRequest(request);
Andreas Papazois2e557be2016-06-04 15:39:56 +0300234 checkReply(reply);
235 return reply;
andreaeb70a942015-10-16 21:34:46 -0700236 }
237
238 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800239 public CompletableFuture<String> request(String request) {
helenyrwu0407c642016-06-09 12:01:30 -0700240 CompletableFuture<String> ftrep = streamHandler.sendMessage(request);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800241 replies.put(messageIdInteger.get(), ftrep);
Andrea Campanella101417d2015-12-11 17:58:07 -0800242 return ftrep;
243 }
244
245 private String sendRequest(String request) throws NetconfException {
246 checkAndRestablishSession();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800247 request = formatRequestMessageId(request);
248 request = formatXmlHeader(request);
Andrea Campanella101417d2015-12-11 17:58:07 -0800249 CompletableFuture<String> futureReply = request(request);
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200250 messageIdInteger.incrementAndGet();
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300251 int replyTimeout = NetconfControllerImpl.netconfReplyTimeout;
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800252 String rp;
253 try {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300254 rp = futureReply.get(replyTimeout, TimeUnit.SECONDS);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800255 } catch (InterruptedException | ExecutionException | TimeoutException e) {
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200256 throw new NetconfException("No matching reply for request " + request, e);
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800257 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800258 log.debug("Result {} from request {} to device {}", rp, request, deviceInfo);
Andrea Campanella50d25212016-02-26 13:06:23 -0800259 return rp.trim();
Andrea Campanella101417d2015-12-11 17:58:07 -0800260 }
261
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800262 private String formatRequestMessageId(String request) {
263 if (request.contains(MESSAGE_ID_STRING)) {
264 //FIXME if application provieds his own counting of messages this fails that count
265 request = request.replaceFirst(MESSAGE_ID_STRING + EQUAL + NUMBER_BETWEEN_QUOTES_MATCHER,
266 MESSAGE_ID_STRING + EQUAL + "\"" + messageIdInteger.get() + "\"");
267 } else if (!request.contains(MESSAGE_ID_STRING) && !request.contains(HELLO)) {
268 //FIXME find out a better way to enforce the presence of message-id
269 request = request.replaceFirst(END_OF_RPC_OPEN_TAG, "\" " + MESSAGE_ID_STRING + EQUAL + "\""
270 + messageIdInteger.get() + "\"" + ">");
271 }
272 return request;
273 }
274
275 private String formatXmlHeader(String request) {
276 if (!request.contains(XML_HEADER)) {
277 //FIXME if application provieds his own XML header of different type there is a clash
278 request = XML_HEADER + "\n" + request;
279 }
280 return request;
281 }
282
Andrea Campanella101417d2015-12-11 17:58:07 -0800283 @Override
284 public String get(String request) throws NetconfException {
285 return requestSync(request);
286 }
287
288 @Override
Akihiro Yamanouchi5e5d4df2016-06-08 17:06:33 +0900289 public String get(String filterSchema, String withDefaultsMode) throws NetconfException {
290 StringBuilder rpc = new StringBuilder(XML_HEADER);
291 rpc.append(RPC_OPEN);
292 rpc.append(MESSAGE_ID_STRING);
293 rpc.append(EQUAL);
294 rpc.append("\"");
295 rpc.append(messageIdInteger.get());
296 rpc.append("\" ");
297 rpc.append(NETCONF_BASE_NAMESPACE).append(">\n");
298 rpc.append(GET_OPEN).append(NEW_LINE);
299 if (filterSchema != null) {
300 rpc.append(FILTER_OPEN).append(NEW_LINE);
301 rpc.append(filterSchema).append(NEW_LINE);
302 rpc.append(FILTER_CLOSE).append(NEW_LINE);
303 }
304 if (withDefaultsMode != null) {
305 rpc.append(WITH_DEFAULT_OPEN).append(NETCONF_WITH_DEFAULTS_NAMESPACE).append(">");
306 rpc.append(withDefaultsMode).append(WITH_DEFAULT_CLOSE).append(NEW_LINE);
307 }
308 rpc.append(GET_CLOSE).append(NEW_LINE);
309 rpc.append(RPC_CLOSE).append(NEW_LINE);
310 rpc.append(ENDPATTERN);
311 String reply = sendRequest(rpc.toString());
312 checkReply(reply);
313 return reply;
314 }
315
316 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800317 public String getConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700318 return getConfig(targetConfiguration, null);
319 }
320
321 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800322 public String getConfig(String targetConfiguration, String configurationSchema) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800323 StringBuilder rpc = new StringBuilder(XML_HEADER);
324 rpc.append("<rpc ");
325 rpc.append(MESSAGE_ID_STRING);
326 rpc.append(EQUAL);
327 rpc.append("\"");
328 rpc.append(messageIdInteger.get());
329 rpc.append("\" ");
330 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700331 rpc.append("<get-config>\n");
332 rpc.append("<source>\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800333 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700334 rpc.append("</source>");
335 if (configurationSchema != null) {
336 rpc.append("<filter type=\"subtree\">\n");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800337 rpc.append(configurationSchema).append("\n");
andreaeb70a942015-10-16 21:34:46 -0700338 rpc.append("</filter>\n");
339 }
340 rpc.append("</get-config>\n");
341 rpc.append("</rpc>\n");
Andrea Campanella101417d2015-12-11 17:58:07 -0800342 rpc.append(ENDPATTERN);
343 String reply = sendRequest(rpc.toString());
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800344 return checkReply(reply) ? reply : "ERROR " + reply;
andreaeb70a942015-10-16 21:34:46 -0700345 }
346
347 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800348 public boolean editConfig(String newConfiguration) throws NetconfException {
349 newConfiguration = newConfiguration + ENDPATTERN;
350 return checkReply(sendRequest(newConfiguration));
andreaeb70a942015-10-16 21:34:46 -0700351 }
352
353 @Override
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800354 public boolean editConfig(String targetConfiguration, String mode, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800355 throws NetconfException {
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800356 newConfiguration = newConfiguration.trim();
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800357 StringBuilder rpc = new StringBuilder(XML_HEADER);
358 rpc.append("<rpc ");
359 rpc.append(MESSAGE_ID_STRING);
360 rpc.append(EQUAL);
361 rpc.append("\"");
362 rpc.append(messageIdInteger.get());
363 rpc.append("\" ");
364 rpc.append("xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
Andrea Campanella50d25212016-02-26 13:06:23 -0800365 rpc.append("<edit-config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800366 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800367 rpc.append("<").append(targetConfiguration).append("/>");
Andrea Campanella50d25212016-02-26 13:06:23 -0800368 rpc.append("</target>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800369 rpc.append("<default-operation>");
370 rpc.append(mode);
Andrea Campanella50d25212016-02-26 13:06:23 -0800371 rpc.append("</default-operation>\n");
372 rpc.append("<config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800373 rpc.append(newConfiguration);
Andrea Campanella50d25212016-02-26 13:06:23 -0800374 rpc.append("</config>\n");
375 rpc.append("</edit-config>\n");
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800376 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800377 rpc.append(ENDPATTERN);
Andrea Campanella50d25212016-02-26 13:06:23 -0800378 log.info(rpc.toString());
Andrea Campanella101417d2015-12-11 17:58:07 -0800379 return checkReply(sendRequest(rpc.toString()));
Andrea Campanellaf4fd0352015-12-14 17:03:05 -0800380 }
381
382 @Override
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800383 public boolean copyConfig(String targetConfiguration, String newConfiguration)
Andrea Campanella101417d2015-12-11 17:58:07 -0800384 throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700385 newConfiguration = newConfiguration.trim();
386 if (!newConfiguration.startsWith("<configuration>")) {
387 newConfiguration = "<configuration>" + newConfiguration
388 + "</configuration>";
389 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800390 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700391 rpc.append("<rpc>");
392 rpc.append("<copy-config>");
393 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800394 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700395 rpc.append("</target>");
396 rpc.append("<source>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800397 rpc.append("<").append(newConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700398 rpc.append("</source>");
399 rpc.append("</copy-config>");
400 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800401 rpc.append(ENDPATTERN);
402 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700403 }
404
405 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800406 public boolean deleteConfig(String targetConfiguration) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700407 if (targetConfiguration.equals("running")) {
408 log.warn("Target configuration for delete operation can't be \"running\"",
409 targetConfiguration);
410 return false;
411 }
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800412 StringBuilder rpc = new StringBuilder(XML_HEADER);
andreaeb70a942015-10-16 21:34:46 -0700413 rpc.append("<rpc>");
414 rpc.append("<delete-config>");
415 rpc.append("<target>");
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800416 rpc.append("<").append(targetConfiguration).append("/>");
andreaeb70a942015-10-16 21:34:46 -0700417 rpc.append("</target>");
418 rpc.append("</delete-config>");
419 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800420 rpc.append(ENDPATTERN);
421 return checkReply(sendRequest(rpc.toString()));
andreaeb70a942015-10-16 21:34:46 -0700422 }
423
424 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700425 public boolean lock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800426 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700427 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700428 rpc.append("<lock>");
429 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700430 rpc.append("<");
431 rpc.append(configType);
432 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700433 rpc.append("</target>");
434 rpc.append("</lock>");
435 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800436 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700437 String lockReply = sendRequest(rpc.toString());
438 return checkReply(lockReply);
andreaeb70a942015-10-16 21:34:46 -0700439 }
440
441 @Override
helenyrwu0407c642016-06-09 12:01:30 -0700442 public boolean unlock(String configType) throws NetconfException {
Andrea Campanellab029b9e2016-01-29 11:05:36 -0800443 StringBuilder rpc = new StringBuilder(XML_HEADER);
helenyrwu0407c642016-06-09 12:01:30 -0700444 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n");
andreaeb70a942015-10-16 21:34:46 -0700445 rpc.append("<unlock>");
446 rpc.append("<target>");
helenyrwu0407c642016-06-09 12:01:30 -0700447 rpc.append("<");
448 rpc.append(configType);
449 rpc.append("/>");
andreaeb70a942015-10-16 21:34:46 -0700450 rpc.append("</target>");
451 rpc.append("</unlock>");
452 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800453 rpc.append(ENDPATTERN);
helenyrwu0407c642016-06-09 12:01:30 -0700454 String unlockReply = sendRequest(rpc.toString());
455 return checkReply(unlockReply);
456 }
457
458 @Override
459 public boolean lock() throws NetconfException {
460 return lock("running");
461 }
462
463 @Override
464 public boolean unlock() throws NetconfException {
465 return unlock("running");
andreaeb70a942015-10-16 21:34:46 -0700466 }
467
468 @Override
Andrea Campanella101417d2015-12-11 17:58:07 -0800469 public boolean close() throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700470 return close(false);
471 }
472
Andrea Campanella101417d2015-12-11 17:58:07 -0800473 private boolean close(boolean force) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700474 StringBuilder rpc = new StringBuilder();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700475 rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
andreaeb70a942015-10-16 21:34:46 -0700476 if (force) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700477 rpc.append("<kill-session/>");
andreaeb70a942015-10-16 21:34:46 -0700478 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700479 rpc.append("<close-session/>");
andreaeb70a942015-10-16 21:34:46 -0700480 }
andreaeb70a942015-10-16 21:34:46 -0700481 rpc.append("</rpc>");
Andrea Campanella101417d2015-12-11 17:58:07 -0800482 rpc.append(ENDPATTERN);
483 return checkReply(sendRequest(rpc.toString())) || close(true);
andreaeb70a942015-10-16 21:34:46 -0700484 }
485
486 @Override
487 public String getSessionId() {
488 if (serverCapabilities.contains("<session-id>")) {
489 String[] outer = serverCapabilities.split("<session-id>");
490 Preconditions.checkArgument(outer.length != 1,
491 "Error in retrieving the session id");
492 String[] value = outer[1].split("</session-id>");
493 Preconditions.checkArgument(value.length != 1,
494 "Error in retrieving the session id");
495 return value[0];
496 } else {
497 return String.valueOf(-1);
498 }
499 }
500
501 @Override
502 public String getServerCapabilities() {
503 return serverCapabilities;
504 }
505
506 @Override
507 public void setDeviceCapabilities(List<String> capabilities) {
508 deviceCapabilities = capabilities;
509 }
510
Andrea Campanella101417d2015-12-11 17:58:07 -0800511 @Override
512 public void addDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700513 streamHandler.addDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800514 }
515
516 @Override
517 public void removeDeviceOutputListener(NetconfDeviceOutputEventListener listener) {
helenyrwu0407c642016-06-09 12:01:30 -0700518 streamHandler.removeDeviceEventListener(listener);
Andrea Campanella101417d2015-12-11 17:58:07 -0800519 }
520
521 private boolean checkReply(String reply) throws NetconfException {
andreaeb70a942015-10-16 21:34:46 -0700522 if (reply != null) {
523 if (!reply.contains("<rpc-error>")) {
524 return true;
525 } else if (reply.contains("<ok/>")
526 || (reply.contains("<rpc-error>")
527 && reply.contains("warning"))) {
528 return true;
529 }
530 }
Andrea Campanellad264b492016-03-01 09:46:06 -0800531 log.warn("Device {} has error in reply {}", deviceInfo, reply);
andreaeb70a942015-10-16 21:34:46 -0700532 return false;
533 }
534
Andrea Campanella101417d2015-12-11 17:58:07 -0800535 public class NetconfSessionDelegateImpl implements NetconfSessionDelegate {
andreaeb70a942015-10-16 21:34:46 -0700536
Andrea Campanella101417d2015-12-11 17:58:07 -0800537 @Override
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200538 public void notify(NetconfDeviceOutputEvent event) {
539 Optional<Integer> messageId = event.getMessageID();
helenyrwu0407c642016-06-09 12:01:30 -0700540
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200541 if (!messageId.isPresent()) {
542 errorReplies.add(event.getMessagePayload());
Andrea Campanellad264b492016-03-01 09:46:06 -0800543 log.error("Device {} sent error reply {}",
544 event.getDeviceInfo(), event.getMessagePayload());
Andreas Papazoisd4712e22016-02-10 15:59:55 +0200545 return;
546 }
547 CompletableFuture<String> completedReply =
548 replies.get(messageId.get());
Andrea Campanella101417d2015-12-11 17:58:07 -0800549 completedReply.complete(event.getMessagePayload());
andreaeb70a942015-10-16 21:34:46 -0700550 }
551 }
andreaeb70a942015-10-16 21:34:46 -0700552}