All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tailf.jnc.Device Maven / Gradle / Ivy

The newest version!
package com.tailf.jnc;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;

/**
 * This class provides features for managing a device with NETCONF.
 * A device has a list of users {@link DeviceUser} which contain information
 * about how to connect to the device.
 * 

* Associated to a device is a list of named sessions, each session is a * {@link NetconfSession} session inside an SSH channel. We can have several * sessions to one device. It can for example make sense to have one session * open to the configuration db(s) and another session open for NETCONF * notifications Typical usage pattern is: *

* *
 * String localUserName = "joe";
 * DeviceUser duser = new DeviceUser(localUserName, "admin", "secret");
 * Device dev = new Device("mydev", duser, "netconf.tail-f.com", 8023);
 * dev.connect(localUserName);
 * dev.newSession("cfg");
 * Element someConfigTree = makeMyTree();
 * dev.getSession("cfg").editConfig(someConfigTree);
 * 
*

* Once we have a new session, we also get a configTree (Element) that we can * use to accumulate changes in for the session. This usually handy since the * manager code typically consists of bits and pieces that manipulate different * parts of the configuration. Then it is convenient to let all manager code * add Elements to the session specifig config tree. The tree can be accessed * through the getConfig(String) method. *

* *

* This class also provides basic backlog funtionality. If the device * configuration is changed and the device is currently down for some reason, a * backlog with the configuration change can be saved and run later when the * device comes up again. *

*/ public class Device implements Serializable { private static final long serialVersionUID = 1L; /** * named session specific connection data */ static private class SessionConnData { String sessionName; SSHSession sshSession; NetconfSession session; SessionConnData(String n, SSHSession t, NetconfSession s) { sessionName = n; session = s; sshSession = t; } } /** * named session specific config data */ static private class SessionTree { String sessionName; Element configTree; SessionTree(String n) { sessionName = n; configTree = null; } } /** * The name of the device, or its IP address. */ public String name; /** * An SSH Connection to this device. */ protected transient SSHConnection con = null; /** * The NETCONF sessions (channels) for this device. */ protected transient ArrayList connSessions; protected transient ArrayList trees; /** * A list of configuration changes. The backlog is saved when a * configuration change is made and the device is down, so that it can be * re-sent later when the device comes up again. */ protected ArrayList backlog; /** * A list of users. */ protected ArrayList users; /** * ip address as string */ protected String mgmt_ip; /** * port number */ protected int mgmt_port; /** * Time to wait for read, in milliseconds. */ protected int defaultReadTimeout = 0; /** * Constructor for the Device with on initial user. We need at least one * DeviceUser in order to be able to connect. */ public Device(String name, DeviceUser user, String mgmt_ip, int mgmt_port) { this.name = name; users = new ArrayList(); users.add(user); this.mgmt_ip = mgmt_ip; this.mgmt_port = mgmt_port; backlog = new ArrayList(); connSessions = new ArrayList(); trees = new ArrayList(); } /** * Constructor for the Device with on initial user. We need at least one * DeviceUser in order to be able to connect. Thus if this constructor is * used to create the Device we must use addUser() priot to connecting. */ public Device(String name, String mgmt_ip, int mgmt_port) { this.name = name; users = new ArrayList(); this.mgmt_ip = mgmt_ip; this.mgmt_port = mgmt_port; backlog = new ArrayList(); connSessions = new ArrayList(); trees = new ArrayList(); } /** * If Device is stored on disk as a serialized object, we need to init the * transient variables after we read a Device from disk. */ public void initTransients() { backlog = new ArrayList(); connSessions = new ArrayList(); trees = new ArrayList(); } /** * Adds a user to the Device list of users. */ public void addUser(DeviceUser user) { users.add(user); } /** * Return the list of users. */ public ArrayList getUsers() { return users; } /** * Clears the accumulation config tree for a named NETCONF session * * @param sessionName symbolic Name of the session */ public void clearConfig(String sessionName) { final SessionTree t = getTreeData(sessionName); t.configTree = null; } /** * Returns the accumulated config tree for a named NETCONF session. This * feature is a convenience feature. It makes sense to perform a series of * changes towards a device and accumulate the changes in a single tree. A * configuration tree is associated to a named session/ssh channel. * * If a session is closed, we may still have accumulated config data * associated to the session. This method retieves this data although the * ssh socket to the server may be dead. * * @param sessionName symbolic Name of the session */ public Element getConfig(String sessionName) { return getTreeData(sessionName).configTree; } /** * Sets the readTimeout associated to a named session * * @param sessionName symbolic Name of the session * @param readTimeout timeout in milliseconds */ public void setReadTimeout(String sessionName, int readTimeout) { final SessionConnData p = getConnData(sessionName); p.sshSession.setReadTimeout(readTimeout); } /** * Gets the readTimeout associated to a named session * * @param sessionName symbolic Name of the session * @return timeout in milliseconds */ public long getReadTimeout(String sessionName) { final SessionConnData p = getConnData(sessionName); return p.sshSession.getReadTimeout(); } /** * Check if the named session have a saved configuration tree. * * @param sessionName symbolic Name of the session */ public boolean hasConfig(String sessionName) { final SessionTree t = getTreeData(sessionName); return t.configTree != null; } /** * Sets the accumulation config tree for a named session * * @param sessionName symbolic Name of the session * @param e The config tree to insert. */ public void setConfig(String sessionName, Element e) { final SessionTree t = getTreeData(sessionName); t.configTree = e; } /** * Checks if a backlog is saved for this device. */ public boolean hasBacklog() { return (backlog.size() > 0); } /** * Whenever new NetconfSession objects are created through newSession() set * this timeout value (milliseconds) as the readTimeout value */ public void setDefaultReadTimeout(int defaultReadTimeout) { this.defaultReadTimeout = defaultReadTimeout; } /** * Close the named session associated with this device. Note, this does not * close the socket, it closes the SSH channel which is a considerably * cheaper operation. The method also clears any accumulated configuration * data we have. * * @param sessionName symbolic Name of the session */ public void closeSession(String sessionName) { final SessionConnData data = removeConnData(sessionName); if (data.session != null) { try { // data.session.closeSession(); data.sshSession.close(); } catch (final Exception e) { } } clearConfig(sessionName); removeTreeData(sessionName); } /** * end all NETCONF sessions and close the SSH socket associated to this * device It also clears all accumulated config trees. */ public void close() { for (int i = 0; i < connSessions.size(); i++) { final SessionConnData d = connSessions.get(i); try { d.session.closeSession(); } catch (final Exception e) { } } connSessions = new ArrayList(); for (int i = 0; i < trees.size(); i++) { final SessionTree t = trees.get(i); t.configTree = null; } trees = new ArrayList(); // we keep the named config trees if (con != null) { con.close(); con = null; } } /** * Checks if this device has any sessions with specified name. */ public boolean hasSession(String name) { return (getSession(name) != null); } /** * Returns a named NetconfSession for this NETCONF enabled device. The * NetconfSession implements (through it's subclass NetconfSession) the * getTransport() method. Thus we can get to the underlying ganymed Session * object as: * *
     * Session s = ((SSHSession) d.getSession("cfg").getTransport()).getSession();
     * 
* * This is required to monitor the ganymed Session object for EOF */ public NetconfSession getSession(String sessionName) { final SessionConnData data = getConnData(sessionName); return (data == null) ? null : data.session; } /** * Returns a named SSHSession for this NETCONF enabled device. We need the * {@link SSHSession} object if we for example wish to check if an ssh * session is ready to read. */ public SSHSession getSSHSession(String sessionName) { final SessionConnData data = getConnData(sessionName); return (data == null) ? null : data.sshSession; } /** * This method finds the {@link DeviceUser} associated to the localUser * user name and SSH connects to the device, this method must be called * prior to establishing any sessions (channels) */ public void connect(String localUser) throws IOException, JNCException { connect(localUser, 0); } /** * This connect() method has an additional timeout paramater. This is not * the same thing as the readTimeout. The connectTimeout only applies to * the actual connect. * * @param localUser The name of a local (for the EMS) user */ public void connect(String localUser, int connectTimeout) throws IOException, JNCException { DeviceUser u = null; for (int i = 0; i < users.size(); i++) { final DeviceUser u2 = users.get(i); if (u2.getLocalUser().equals(localUser)) { u = u2; break; } } if (u == null) { throw new JNCException(JNCException.AUTH_FAILED, "No such user: " + localUser); } con = new SSHConnection(mgmt_ip, mgmt_port, connectTimeout); auth(u); } public boolean isConnect(){ if(con == null ||con.isClose()) return false; return true; } /** * This method is mostly interesting if we want to use the backlog * functionality. A typical sequence towards a device is connect(), * newSession(). If connect fails, because the device is down, we still * might want to have a config tree to accumulate changes in although we * have no connection to the device. The getConfig(String) method searches * in a list of configTrees, thus we can have named configTrees although * there is no existing session towards the device. */ public void newSessionConfigTree(String sessionName) { if (getTreeData(sessionName) == null) { trees.add(new SessionTree(sessionName)); } } /** * Creates a new named NETCONF session. Each named session corresponds to * one SSH channel towards the agent * * @param sessionName symbolic Name of the session */ public void newSession(String sessionName) throws JNCException, IOException, YangException { newSession(null, sessionName); } /** * Creates a new named NETCONF session We must not have an existing session * with the same name. Thus when we reconnect a Device, we must first call * closeSession(sessionName); * * @param sub IO subscriber for trace messages. * @param sessionName symbolic Name of the session */ public void newSession(IOSubscriber sub, String sessionName) throws JNCException, IOException { final SessionConnData data = getConnData(sessionName); // always create the configTree newSessionConfigTree(sessionName); // Check that we don't already have a session with that name if (data != null) { int errCode = YangException.BAD_SESSION_NAME; throw new YangException(errCode, sessionName); } final YangXMLParser parser = new YangXMLParser(); final SSHSession sshSession = new SSHSession(con, defaultReadTimeout); if (sub != null) { sshSession.addSubscriber(sub); } final NetconfSession session = new NetconfSession(sshSession, parser); final SessionConnData d = new SessionConnData(sessionName, sshSession, session); connSessions.add(d); if (backlog.size() > 0) { runBacklog(sessionName); } } /** * Adds the given configuration tree to the list of backlogs. * * @param e Config tree to be saved. */ public void addBackLog(Element e) { backlog.add(e); } /** * Return the backlog. */ public Element[] getBacklog() { if (backlog != null && backlog.size() > 0) { final Element[] a = new Element[backlog.size()]; return backlog.toArray(a); } return null; } /** * Run the backlog. A backlog is typically saved when a configuration * change is made (for several devices) and the device is down. The backlog * saves the configuration update so that it can be re-sent later. This * method runs the saved backlog. It is automatically run whenever we * succeeed in creating a new sesssion. * * @param sessionName symbolic Name of the session */ public void runBacklog(String sessionName) throws IOException, JNCException { System.out.println("Running backlog "); final SessionConnData data = getConnData(sessionName); if (data == null) { throw new YangException(YangException.BAD_SESSION_NAME, sessionName); } for (int i = 0; i < backlog.size(); i++) { final Element e = backlog.get(i); System.out.println("Bacloh " + e.toXMLString()); data.session.editConfig(e); backlog.remove(i); } } /** * Returns a string with information about this device. */ @Override public String toString() { StringBuffer s = new StringBuffer("Device: " + name + " " + mgmt_ip + ":" + mgmt_port + "\n"); for (final SessionConnData p : connSessions) { s.append(" session: ").append(p.sessionName); } return s.toString(); } private SessionConnData getConnData(String sessionName) { for (final SessionConnData p : connSessions) { if (sessionName.equals(p.sessionName)) { return p; } } return null; } private SessionTree getTreeData(String sessionName) { for (final SessionTree t : trees) { if ( t!=null && sessionName.equals(t.sessionName)) { return t; } } return null; } private SessionConnData removeConnData(String sessionName) { for (int i = 0; i < connSessions.size(); i++) { final SessionConnData p = connSessions.get(i); if (sessionName.equals(p.sessionName)) { connSessions.remove(i); return p; } } return null; } private SessionTree removeTreeData(String sessionName) { for (int i = 0; i < trees.size(); i++) { final SessionTree t = trees.get(i); if (t!=null && sessionName.equals(t.sessionName)) { trees.remove(i); return t; } } return null; } private void auth(DeviceUser currentUser) throws IOException, JNCException { if (currentUser.getPassword() != null) { con.authenticateWithPassword( currentUser.getRemoteuser(), currentUser.getPassword()); } else if (currentUser.getPemPrivateKey() != null) { con.authenticateWithPublicKey( currentUser.getRemoteuser(), currentUser.getPemPrivateKey(), currentUser.getKeyPassPhrase()); } else if (currentUser.getPemFile() != null) { con.authenticateWithPublicKeyFile( currentUser.getRemoteuser(), currentUser.getPemFile(), currentUser.getKeyPassPhrase()); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy