/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.netconf.ctl; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import com.google.common.base.Preconditions; import org.onosproject.netconf.NetconfDeviceInfo; import org.onosproject.netconf.NetconfSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Implementation of a NETCONF session to talk to a device. */ public class NetconfSessionImpl implements NetconfSession { public static final Logger log = LoggerFactory .getLogger(NetconfSessionImpl.class); private static final int CONNECTION_TIMEOUT = 0; private Connection netconfConnection; private NetconfDeviceInfo deviceInfo; private Session sshSession; private boolean connectionActive; private BufferedReader bufferReader = null; private PrintWriter out = null; private int messageID = 0; private List deviceCapabilities = new ArrayList<>( Arrays.asList("urn:ietf:params:netconf:base:1.0")); private String serverCapabilities; private String endpattern = "]]>]]>"; public NetconfSessionImpl(NetconfDeviceInfo deviceInfo) throws IOException { this.deviceInfo = deviceInfo; connectionActive = false; startConnection(); } private void startConnection() throws IOException { if (!connectionActive) { netconfConnection = new Connection(deviceInfo.ip().toString(), deviceInfo.port()); netconfConnection.connect(null, CONNECTION_TIMEOUT, 0); boolean isAuthenticated; try { if (deviceInfo.getKeyFile() != null) { isAuthenticated = netconfConnection.authenticateWithPublicKey( deviceInfo.name(), deviceInfo.getKeyFile(), deviceInfo.password()); } else { log.info("authenticate with username {} and password {}", deviceInfo.name(), deviceInfo.password()); isAuthenticated = netconfConnection.authenticateWithPassword( deviceInfo.name(), deviceInfo.password()); } } catch (IOException e) { throw new IOException("Authentication connection failed:" + e.getMessage()); } connectionActive = true; Preconditions.checkArgument(isAuthenticated, "Authentication password and username failed"); startSshSession(); } } private void startSshSession() throws IOException { try { sshSession = netconfConnection.openSession(); sshSession.startSubSystem("netconf"); bufferReader = new BufferedReader(new InputStreamReader( sshSession.getStdout())); out = new PrintWriter(sshSession.getStdin()); sendHello(); } catch (IOException e) { throw new IOException("Failed to create ch.ethz.ssh2.Session session:" + e.getMessage()); } } private void sendHello() throws IOException { serverCapabilities = doRequest(createHelloString()); } private String createHelloString() { StringBuilder hellobuffer = new StringBuilder(); hellobuffer.append("\n"); hellobuffer.append("\n"); hellobuffer.append(" \n"); deviceCapabilities.forEach( cap -> hellobuffer.append(" " + cap + "\n")); hellobuffer.append(" \n"); hellobuffer.append("\n"); hellobuffer.append(endpattern); return hellobuffer.toString(); } @Override public String doRPC(String request) { String reply = "ERROR"; try { reply = doRequest(request); if (checkReply(reply)) { return reply; } else { return "ERROR " + reply; } } catch (IOException e) { log.error("Problem in the reading from the SSH connection " + e); } return reply; } private String doRequest(String request) throws IOException { log.info("sshState " + sshSession.getState() + "request" + request); if (sshSession.getState() != 2) { try { startSshSession(); } catch (IOException e) { log.info("the connection had to be reopened"); startConnection(); } sendHello(); } log.info("sshState after" + sshSession.getState()); out.print(request); out.flush(); messageID++; return readOne(); } @Override public String get(String request) { return doRPC(request); } @Override public String getConfig(String targetConfiguration) { return getConfig(targetConfiguration, null); } @Override public String getConfig(String targetConfiguration, String configurationSchema) { StringBuilder rpc = new StringBuilder(""); rpc.append("\n"); rpc.append("\n"); rpc.append("\n"); rpc.append("<" + targetConfiguration + "/>"); rpc.append(""); if (configurationSchema != null) { rpc.append("\n"); rpc.append(configurationSchema + "\n"); rpc.append("\n"); } rpc.append("\n"); rpc.append("\n"); rpc.append(endpattern); String reply = null; try { reply = doRequest(rpc.toString()); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply) ? reply : null; } @Override public boolean editConfig(String newConfiguration) { newConfiguration = newConfiguration + endpattern; String reply = null; try { reply = doRequest(newConfiguration); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply); } @Override public boolean copyConfig(String targetConfiguration, String newConfiguration) { newConfiguration = newConfiguration.trim(); if (!newConfiguration.startsWith("")) { newConfiguration = "" + newConfiguration + ""; } StringBuilder rpc = new StringBuilder(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append("<" + targetConfiguration + "/>"); rpc.append(""); rpc.append(""); rpc.append("<" + newConfiguration + "/>"); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(endpattern); String reply = null; try { reply = doRequest(rpc.toString()); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply); } @Override public boolean deleteConfig(String targetConfiguration) { if (targetConfiguration.equals("running")) { log.warn("Target configuration for delete operation can't be \"running\"", targetConfiguration); return false; } StringBuilder rpc = new StringBuilder(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append("<" + targetConfiguration + "/>"); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(endpattern); String reply = null; try { reply = doRequest(rpc.toString()); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply); } @Override public boolean lock() { StringBuilder rpc = new StringBuilder(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(endpattern); String reply = null; try { reply = doRequest(rpc.toString()); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply); } @Override public boolean unlock() { StringBuilder rpc = new StringBuilder(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(""); rpc.append(endpattern); String reply = null; try { reply = doRequest(rpc.toString()); } catch (IOException e) { e.printStackTrace(); } return checkReply(reply); } @Override public boolean close() { return close(false); } private boolean close(boolean force) { StringBuilder rpc = new StringBuilder(); rpc.append(""); if (force) { rpc.append(""); } else { rpc.append(""); } rpc.append(""); rpc.append(""); rpc.append(endpattern); return checkReply(rpc.toString()) ? true : close(true); } @Override public String getSessionId() { if (serverCapabilities.contains("")) { String[] outer = serverCapabilities.split(""); Preconditions.checkArgument(outer.length != 1, "Error in retrieving the session id"); String[] value = outer[1].split(""); Preconditions.checkArgument(value.length != 1, "Error in retrieving the session id"); return value[0]; } else { return String.valueOf(-1); } } @Override public String getServerCapabilities() { return serverCapabilities; } @Override public void setDeviceCapabilities(List capabilities) { deviceCapabilities = capabilities; } private boolean checkReply(String reply) { if (reply != null) { if (!reply.contains("")) { return true; } else if (reply.contains("") || (reply.contains("") && reply.contains("warning"))) { return true; } } return false; } private String readOne() throws IOException { //TODO try a simple string final StringWriter reply = new StringWriter(); while (true) { int charRead = bufferReader.read(); if (charRead == -1) { throw new IOException("Session closed"); } for (int i = 0; i < endpattern.length(); i++) { if (charRead == endpattern.charAt(i)) { if (i < endpattern.length() - 1) { charRead = bufferReader.read(); } else { return reply.getBuffer().toString(); } } else { String s = endpattern.substring(0, i); for (int j = 0; i < s.length(); j++) { reply.write(s.charAt(j)); } reply.write(charRead); break; } } } } }