之前详细介绍了Neconfig网络配置协议的一些概念,没看过的小伙伴可以移步NetConfig协议详细介绍了解一下。Java作为应用最广泛的语言之一,今天主要讲Netconfig协议怎么在Java中使用。
JAVA实现Netconfig协议
1、进行Java开发之前,支持Netconfig协议的设备需要先开启SSH以及Netconfig使能。
2、Netconfig使用ssh协议进行远程连接,因此在java中需要使用ssh协议的jar包,本文使用是jsch-0.1.55.jar。Maven项目中可以如下配置:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<vers之ion>0.1.55</version>
</dependency>
3、代码实现
直接上代码了,代码有点多,有兴趣的小伙伴可以粘贴到本地尝试一下。不过各个厂商的设备有些不同,我用的是东土的设备,关于组装xml命令的有些内容可以根据具体设备修改一下。
首先创建一个实体类Device当做客户端
package com.example.demo.netconfig;
import com.jcraft.jsch.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Getter
public class Device {
private static final int DEFAULT_NETCONF_PORT = 830;
private static final int DEFAULT_TIMEOUT = 3000;
private String hostName;
private int port;
private int timeout;
private String userName;
private String password;
private boolean keyBasedAuthentication;
private String pemKeyFile;
private boolean strictHostKeyChecking;
private String hostKeysFileName;
private JSch sshClient;
private ChannelSubsystem sshChannel;
private Session sshSession;
private DocumentBuilder builder;
private NetconfSession netconfSession;
private List<String> netconfCapabilities;
private String helloRpc;
@Builder
public Device(
@NonNull String hostName,
String password,
Boolean keyBasedAuthentication,
String pemKeyFile,
Integer port,
Integer timeout,
@NonNull String userName,
Boolean strictHostKeyChecking,
String hostKeysFileName,
List<String> netconfCapabilities
) throws IOException {
this.hostName = hostName;
this.port = (port != null) ? port : DEFAULT_NETCONF_PORT;
this.timeout = (timeout != null) ? timeout : DEFAULT_TIMEOUT;
this.userName = userName;
this.password = password;
if (this.password == null && pemKeyFile == null) {
throw new IOException("Auth requires either setting the password or the pemKeyFile");
}
this.keyBasedAuthentication = (keyBasedAuthentication != null) ? keyBasedAuthentication : false;
this.pemKeyFile = pemKeyFile;
if (this.keyBasedAuthentication && pemKeyFile == null) {
throw new IOException("key based authentication requires setting the pemKeyFile");
}
this.strictHostKeyChecking = (strictHostKeyChecking != null) ? strictHostKeyChecking : true;
this.hostKeysFileName = hostKeysFileName;
if (this.strictHostKeyChecking && hostKeysFileName == null) {
throw new IOException("Strict Host Key checking requires setting the hostKeysFileName");
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new IOException(String.format("Error creating XML Parser: %s", e.getMessage()));
}
this.netconfCapabilities = (netconfCapabilities != null) ? netconfCapabilities : getDefaultClientCapabilities();
this.helloRpc = createHelloRPC(this.netconfCapabilities);
this.sshClient = new JSch();
}
private List<String> getDefaultClientCapabilities() {
List<String> defaultCap = new ArrayList<>();
defaultCap.add(NetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0);
defaultCap.add(NetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0 + "#candidate");
defaultCap.add(NetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0 + "#confirmed-commit");
defaultCap.add(NetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0 + "#validate");
defaultCap.add(NetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0 + "#url?protocol=http,ftp,file");
return defaultCap;
}
private String createHelloRPC(List<String> capabilities) {
StringBuilder helloRPC = new StringBuilder();
helloRPC.append("<hello xmlns=\"" + NetconfConstants.URN_XML_NS_NETCONF_BASE_1_0 + "\">\n");
helloRPC.append("<capabilities>\n");
for (Object o : capabilities) {
String capability = (String) o;
helloRPC
.append("<capability>")
.append(capability)
.append("</capability>\n");
}
helloRPC.append("</capabilities>\n");
helloRPC.append("</hello>\n");
helloRPC.append(NetconfConstants.DEVICE_PROMPT);
return helloRPC.toString();
}
private NetconfSession createNetconfSession() throws IOException {
if (!isConnected()) {
sshClient = new JSch();
try {
if (strictHostKeyChecking) {
if (hostKeysFileName == null) {
throw new IOException("Cannot do strictHostKeyChecking if hostKeysFileName is null");
}
sshClient.setKnownHosts(hostKeysFileName);
}
} catch (JSchException e) {
throw new IOException(String.format("Error loading known hosts file: %s", e.getMessage()));
}
sshClient.setHostKeyRepository(sshClient.getHostKeyRepository());
//log.info("Connecting to host {} on port {}.", hostName, port);
System.out.println("Connecting to host {} on port {}."+ hostName+ port);
if (keyBasedAuthentication) {
sshSession = loginWithPrivateKey(timeout);
loadPrivateKey();
} else {
sshSession = loginWithUserPass(timeout);
}
try {
sshSession.setTimeout(timeout);
} catch (JSchException e) {
throw new IOException(String.format("Error setting session timeout: %s", e.getMessage()));
}
if (sshSession.isConnected()) {
//log.info("Connected to host {} - Timeout set to {} msecs.", hostName, sshSession.getTimeout());
System.out.println("Connected to host {} - Timeout set to {} msecs."+hostName+ sshSession.getTimeout());
} else {
throw new IOException("Failed to connect to host. Unknown reason");
}
}
try {
sshChannel = (ChannelSubsystem) sshSession.openChannel("subsystem");
sshChannel.setSubsystem("netconf");
return new NetconfSession(sshChannel, timeout, helloRpc, builder);
} catch (JSchException | IOException e) {
throw new IOException("Failed to create Netconf session:" +
e.getMessage());
}
}
private Session loginWithUserPass(int timeoutMilliSeconds) throws IOException {
try {
Session session = sshClient.getSession(userName, hostName, port);
session.setConfig("userauth", "password");
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(password);
session.connect(timeoutMilliSeconds);
return session;
} catch (JSchException e) {
throw new IOException(String.format("Error connecting to host: %s - Error: %s",
hostName, e.getMessage()));
}
}
private Session loginWithPrivateKey(int timeoutMilliSeconds) throws IOException {
try {
Session session = sshClient.getSession(userName, hostName, port);
session.setConfig("userauth", "publickey");
session.setConfig("StrictHostKeyChecking", "no");
session.connect(timeoutMilliSeconds);
return session;
} catch (JSchException e) {
throw new IOException(String.format("Error using key pair file: %s to connect to host: %s - Error: %s",
pemKeyFile, hostName, e.getMessage()));
}
}
public void connect() throws IOException {
if (hostName == null || userName == null || (password == null &&
pemKeyFile == null)) {
throw new IOException("Login parameters of Device can't be " +
"null.");
}
netconfSession = this.createNetconfSession();
}
private void loadPrivateKey() throws IOException {
try {
sshClient.addIdentity(pemKeyFile);
} catch (JSchException e) {
throw new IOException(String.format("Error parsing the pemKeyFile: %s", e.getMessage()));
}
}
public String reboot() throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.reboot();
}
public boolean isConnected() {
return (isChannelConnected() && isSessionConnected());
}
private boolean isChannelConnected() {
if (sshChannel == null) {
return false;
}
return sshChannel.isConnected();
}
private boolean isSessionConnected() {
if (sshSession == null) {
return false;
}
return sshSession.isConnected();
}
public void close() {
if (!isConnected()) {
return;
}
sshChannel.disconnect();
sshSession.disconnect();
}
public String runShellCommand(String command) throws IOException {
if (!isConnected()) {
return "Could not find open connection.";
}
ChannelExec channel;
try {
channel = (ChannelExec) sshSession.openChannel("exec");
} catch (JSchException e) {
throw new IOException(String.format("Failed to open exec session: %s", e.getMessage()));
}
channel.setCommand(command);
InputStream stdout;
BufferedReader bufferReader;
stdout = channel.getInputStream();
bufferReader = new BufferedReader(new InputStreamReader(stdout, Charset.defaultCharset()));
try {
StringBuilder reply = new StringBuilder();
while (true) {
String line;
try {
line = bufferReader.readLine();
} catch (Exception e) {
throw new IOException(e.getMessage());
}
if (line == null || line.equals(""))
break;
reply.append(line).append("\n");
}
return reply.toString();
} finally {
bufferReader.close();
}
}
public BufferedReader runShellCommandRunning(String command)
throws IOException {
if (!isConnected()) {
throw new IOException("Could not find open connection");
}
ChannelExec channel;
try {
channel = (ChannelExec) sshSession.openChannel("exec");
} catch (JSchException e) {
throw new IOException(String.format("Failed to open exec session: %s", e.getMessage()));
}
InputStream stdout = channel.getInputStream();
return new BufferedReader(new InputStreamReader(stdout, Charset.defaultCharset()));
}
public XML executeRPC(String rpcContent) throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPC(rpcContent);
}
public XML executeRPC(XML rpc) throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPC(rpc);
}
public XML executeRPC(Document rpcDoc) throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPC(rpcDoc);
}
public BufferedReader executeRPCRunning(String rpcContent) throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPCRunning(rpcContent);
}
public BufferedReader executeRPCRunning(XML rpc) throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPCRunning(rpc);
}
public BufferedReader executeRPCRunning(Document rpcDoc) throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.executeRPCRunning(rpcDoc);
}
public String getSessionId() {
if (netconfSession == null) {
throw new IllegalStateException("Cannot get session ID, you need " +
"to establish a connection first.");
}
return this.netconfSession.getSessionId();
}
public boolean hasError() throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("No RPC executed yet, you need to" +
" establish a connection first.");
}
return this.netconfSession.hasError();
}
public boolean hasWarning() throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("No RPC executed yet, you need to " +
"establish a connection first.");
}
return this.netconfSession.hasWarning();
}
public boolean isOK() {
if (netconfSession == null) {
throw new IllegalStateException("No RPC executed yet, you need to " +
"establish a connection first.");
}
return this.netconfSession.isOK();
}
public boolean lockConfig() throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.lockConfig();
}
public boolean unlockConfig() throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.unlockConfig();
}
public void loadXMLConfiguration(String configuration, String loadType)
throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadXMLConfiguration(configuration, loadType);
}
public void loadTextConfiguration(String configuration, String loadType)
throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadTextConfiguration(configuration, loadType);
}
public void loadSetConfiguration(String configuration) throws
IOException,
SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadSetConfiguration(configuration);
}
public void loadXMLFile(String configFile, String loadType)
throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadXMLFile(configFile, loadType);
}
public void loadTextFile(String configFile, String loadType)
throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadTextFile(configFile, loadType);
}
public void loadSetFile(String configFile) throws
IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.loadSetFile(configFile);
}
public void commit() throws IOException, IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.commit();
}
public void commitConfirm(long seconds) throws IOException,
SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.commitConfirm(seconds);
}
public void commitFull() throws IOException, IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.commitFull();
}
public void commitThisConfiguration(String configFile, String loadType)
throws IOException, IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
this.netconfSession.commitThisConfiguration(configFile, loadType);
}
public XML getCandidateConfig(String configTree) throws SAXException,
IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.getCandidateConfig(configTree);
}
public XML getRunningConfig(String configTree) throws SAXException,
IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.getRunningConfig(configTree);
}
public XML getCandidateConfig() throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.getCandidateConfig();
}
public XML getRunningConfig() throws SAXException, IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.getRunningConfig();
}
public boolean validate() throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.validate();
}
public String runCliCommand(String command) throws IOException, SAXException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.runCliCommand(command);
}
public BufferedReader runCliCommandRunning(String command) throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
return this.netconfSession.runCliCommandRunning(command);
}
public void openConfiguration(String mode) throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
netconfSession.openConfiguration(mode);
}
public void closeConfiguration() throws IOException {
if (netconfSession == null) {
throw new IllegalStateException("Cannot execute RPC, you need to " +
"establish a connection first.");
}
netconfSession.closeConfiguration();
}
public String getLastRPCReply() {
return this.netconfSession.getLastRPCReply();
}
}
创建一个CreateDevice类型表示创建连接。
package com.example.demo.netconfig;
import java.io.IOException;
public class CreateDevice {
private static final String HOSTNAME = "192.168.2.75";
private static final String USERNAME = "admin";
private static final String PASSWORD = "123";
private static final String PEM_KEY_FILE_PATH = "/tmp/pemFile";
public static Device createDevice() throws IOException {
return Device.builder()
.hostName(HOSTNAME)
.userName(USERNAME)
.password(PASSWORD)
.strictHostKeyChecking(false)
.build();
}
public static Device createDevice(String hostName, String userName, String passWord) throws IOException {
return Device.builder()
.hostName(hostName)
.userName(userName)
.password(passWord)
.strictHostKeyChecking(false)
.build();
}
public static Device createDeviceWithKeyAuth(String keyFile) throws IOException {
return Device.builder()
.hostName(HOSTNAME)
.userName(USERNAME)
.pemKeyFile(PEM_KEY_FILE_PATH)
.strictHostKeyChecking(false)
.build();
}
public static Device createDeviceWithKeyAuth(String hostName, String userName, String keyFile) throws IOException {
return Device.builder()
.hostName(hostName)
.userName(userName)
.pemKeyFile(keyFile)
.strictHostKeyChecking(false)
.build();
}
}
创建一个NetconfSession类表示会话。
package com.example.demo.netconfig;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSchException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class NetconfSession {
private final Channel netconfChannel;
private String serverCapability;
private InputStream stdInStreamFromDevice;
private OutputStream stdOutStreamToDevice;
private String lastRpcReply;
private final DocumentBuilder builder;
private int messageId = 0;
private static final String CANDIDATE_CONFIG = "candidate";
private static final String EMPTY_CONFIGURATION_TAG = "<configuration></configuration>";
private static final String RUNNING_CONFIG = "running";
private static final String NETCONF_SYNTAX_ERROR_MSG_FROM_DEVICE = "netconf error: syntax error";
NetconfSession(Channel netconfChannel, int timeout, String hello,
DocumentBuilder builder) throws IOException {
stdInStreamFromDevice = netconfChannel.getInputStream();
stdOutStreamToDevice = netconfChannel.getOutputStream();
try {
netconfChannel.connect(timeout);
} catch (JSchException e) {
throw new IOException("Failed to create Netconf session:" +
e.getMessage());
}
this.netconfChannel = netconfChannel;
this.builder = builder;
sendHello(hello);
}
private XML convertToXML(String xml) throws SAXException, IOException {
if (xml.contains(NETCONF_SYNTAX_ERROR_MSG_FROM_DEVICE)) {
throw new IOException(String.format("Netconf server detected an error: %s", xml));
}
Document doc = builder.parse(new InputSource(new StringReader(xml)));
Element root = doc.getDocumentElement();
return new XML(root);
}
private void sendHello(String hello) throws IOException {
String reply = getRpcReply(hello);
serverCapability = reply;
lastRpcReply = reply;
}
@VisibleForTesting
String getRpcReply(String rpc) throws IOException {
BufferedReader bufferedReader = getRpcReplyRunning(rpc);
StringBuilder rpcReply = new StringBuilder();
while (!rpcReply.toString().contains(NetconfConstants.DEVICE_PROMPT)) {
int line = bufferedReader.read();
if (line == -1) {
throw new IOException("Input Stream has been closed during reading.");
}
rpcReply.append((char) line);
}
String reply = rpcReply.toString().replace(NetconfConstants.DEVICE_PROMPT, "");
System.out.println("Received Netconf RPC-Reply\n{}"+reply);
return reply;
}
private BufferedReader getRpcReplyRunning(String rpc) throws IOException {
messageId++;
rpc = rpc.replace("<rpc>", "<rpc xmlns=\"" + NetconfConstants.URN_XML_NS_NETCONF_BASE_1_0 + "\" message-id=\"" + messageId + "\">").trim();
if (!rpc.contains(NetconfConstants.XML_VERSION)) {
rpc = NetconfConstants.XML_VERSION + rpc;
}
System.out.println("Sending Netconf RPC\n{}"+rpc);
stdOutStreamToDevice.write(rpc.getBytes(Charsets.UTF_8));
stdOutStreamToDevice.flush();
return new BufferedReader(
new InputStreamReader(stdInStreamFromDevice, Charsets.UTF_8));
}
public void loadXMLConfiguration(String configuration, String loadType) throws IOException, SAXException {
validateLoadType(loadType);
configuration = configuration.trim();
if (!configuration.startsWith("<configuration")) {
configuration = "<configuration>" + configuration
+ "</configuration>";
}
String rpc = "<rpc>" +
"<edit-config>" +
"<target>" +
"<" + CANDIDATE_CONFIG + "/>" +
"</target>" +
"<default-operation>" +
loadType +
"</default-operation>" +
"<config>" +
configuration +
"</config>" +
"</edit-config>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Load operation returned error.");
}
public void loadTextConfiguration(String configuration, String loadType) throws IOException, SAXException {
String rpc = "<rpc>" +
"<edit-config>" +
"<target>" +
"<" + CANDIDATE_CONFIG + "/>" +
"</target>" +
"<default-operation>" +
loadType +
"</default-operation>" +
"<config-text>" +
"<configuration-text>" +
configuration +
"</configuration-text>" +
"</config-text>" +
"</edit-config>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Load operation returned error");
}
private String getConfig(String configTree) throws IOException {
String rpc = "<rpc>" +
"<get-config>" +
"<source>" +
"<" + CANDIDATE_CONFIG + "/>" +
"</source>" +
"<filter type=\"subtree\">" +
configTree +
"</filter>" +
"</get-config>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
return lastRpcReply;
}
private String getConfig(String target, String configTree)
throws IOException {
String rpc = "" + "<rpc>" +
"<get-config>" +
"<source>" +
"<" + target + "/>" +
"</source>" +
"<filter type=\"subtree\">" +
configTree +
"</filter>" +
"</get-config>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
return lastRpcReply;
}
public String getServerCapability() {
return serverCapability;
}
public XML executeRPC(String rpcContent) throws SAXException, IOException {
String rpc=fixupRpc(rpcContent);
String rpcReply = getRpcReply(fixupRpc(rpcContent));
lastRpcReply = rpcReply;
return convertToXML(rpcReply);
}
public XML executeRPC(XML rpc) throws SAXException, IOException {
return executeRPC(rpc.toString());
}
public XML executeRPC(Document rpcDoc) throws SAXException, IOException {
Element root = rpcDoc.getDocumentElement();
XML xml = new XML(root);
return executeRPC(xml);
}
@VisibleForTesting
static String fixupRpc(@NonNull String rpcContent) throws IllegalArgumentException {
if (rpcContent == null) {
throw new IllegalArgumentException("Null RPC");
}
rpcContent = rpcContent.trim();
if (!rpcContent.startsWith("<rpc>") && !rpcContent.equals("<rpc/>")) {
if (rpcContent.startsWith("<"))
rpcContent = "<rpc>" + rpcContent + "</rpc>";
else
rpcContent = "<rpc>" + "<" + rpcContent + "/>" + "</rpc>";
}
return rpcContent + NetconfConstants.DEVICE_PROMPT;
}
public BufferedReader executeRPCRunning(String rpcContent) throws IOException {
return getRpcReplyRunning(fixupRpc(rpcContent));
}
public BufferedReader executeRPCRunning(XML rpc) throws IOException {
return executeRPCRunning(rpc.toString());
}
public BufferedReader executeRPCRunning(Document rpcDoc) throws IOException {
Element root = rpcDoc.getDocumentElement();
XML xml = new XML(root);
return executeRPCRunning(xml);
}
public String getSessionId() {
String[] split = serverCapability.split("<session-id>");
if (split.length != 2)
return null;
String[] idSplit = split[1].split("</session-id>");
if (idSplit.length != 2)
return null;
return idSplit[0];
}
public void close() throws IOException {
String rpc = "<rpc>" +
"<close-session/>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
netconfChannel.disconnect();
}
public boolean hasError() throws SAXException, IOException {
if (lastRpcReply == null || !(lastRpcReply.contains("<rpc-error>")))
return false;
String errorSeverity = parseForErrors(lastRpcReply);
return errorSeverity != null && errorSeverity.equals("error");
}
private String parseForErrors(String inputXmlReply) throws IOException, SAXException {
XML xmlReply = convertToXML(lastRpcReply);
List<String> tagList = new ArrayList<>();
tagList.add("rpc-error");
tagList.add("error-severity");
return xmlReply.findValue(tagList);
}
public boolean hasWarning() throws SAXException, IOException {
if (lastRpcReply == null || !(lastRpcReply.contains("<rpc-error>")))
return false;
String errorSeverity = parseForErrors(lastRpcReply);
return errorSeverity != null && errorSeverity.equals("warning");
}
public boolean isOK() {
return lastRpcReply != null && lastRpcReply.contains("<ok/>");
}
public boolean lockConfig() throws IOException, SAXException {
String rpc = "<rpc>" +
"<lock>" +
"<target>" +
"<candidate/>" +
"</target>" +
"</lock>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
return !hasError() && isOK();
}
public boolean unlockConfig() throws IOException, SAXException {
String rpc = "<rpc>" +
"<unlock>" +
"<target>" +
"<candidate/>" +
"</target>" +
"</unlock>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
return !hasError() && isOK();
}
public void loadSetConfiguration(String configuration) throws IOException, SAXException {
String rpc = "<rpc>" +
"<load-configuration action=\"set\">" +
"<configuration-set>" +
configuration +
"</configuration-set>" +
"</load-configuration>" +
"</rpc>";
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Load operation returned error");
}
public void loadXMLFile(String configFile, String loadType) throws IOException, SAXException {
validateLoadType(loadType);
loadXMLConfiguration(readConfigFile(configFile), loadType);
}
private void validateLoadType(String loadType) throws IllegalArgumentException {
if (loadType == null || (!loadType.equals("merge") &&
!loadType.equals("replace")))
throw new IllegalArgumentException("'loadType' argument must be " +
"merge|replace");
}
private String readConfigFile(String configFile) throws IOException {
try {
return new String(Files.readAllBytes(Paths.get(configFile)), Charset.defaultCharset().name());
} catch (FileNotFoundException e) {
throw new FileNotFoundException("The system cannot find the configuration file specified: " + configFile);
}
}
public void loadTextFile(String configFile, String loadType) throws IOException, SAXException {
validateLoadType(loadType);
loadTextConfiguration(readConfigFile(configFile), loadType);
}
public void loadSetFile(String configFile) throws
IOException, SAXException {
loadSetConfiguration(readConfigFile(configFile));
}
public void commitThisConfiguration(String configFile, String loadType) throws IOException, SAXException {
String configuration = readConfigFile(configFile);
configuration = configuration.trim();
if (this.lockConfig()) {
if (configuration.startsWith("<")) {
this.loadXMLConfiguration(configuration, loadType);
} else if (configuration.startsWith("set")) {
this.loadSetConfiguration(configuration);
} else {
this.loadTextConfiguration(configuration, loadType);
}
this.commit();
} else {
throw new IOException("Unclean lock operation. Cannot proceed " +
"further.");
}
this.unlockConfig();
}
public void commit() throws IOException, SAXException {
String rpc = "<rpc>" +
"<commit/>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Commit operation returned error.");
}
public void commitConfirm(long seconds) throws IOException, SAXException {
String rpc = "<rpc>" +
"<commit>" +
"<confirmed/>" +
"<confirm-timeout>" + seconds + "</confirm-timeout>" +
"</commit>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Commit operation returned " +
"error.");
}
public void commitFull() throws IOException, IOException, SAXException {
String rpc = "<rpc>" +
"<commit-configuration>" +
"<full/>" +
"</commit-configuration>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
if (hasError() || !isOK())
throw new IOException("Commit operation returned error.");
}
public XML getCandidateConfig(String configTree) throws SAXException,
IOException {
return convertToXML(getConfig(configTree));
}
public XML getRunningConfig(String configTree) throws SAXException,
IOException {
return convertToXML(getConfig(RUNNING_CONFIG, configTree));
}
public XML getCandidateConfig() throws SAXException, IOException {
return convertToXML(getConfig(EMPTY_CONFIGURATION_TAG));
}
public XML getRunningConfig() throws SAXException, IOException {
return convertToXML(getConfig(RUNNING_CONFIG, EMPTY_CONFIGURATION_TAG));
}
public boolean validate() throws IOException, SAXException {
String rpc = "<rpc>" +
"<validate>" +
"<source>" +
"<candidate/>" +
"</source>" +
"</validate>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
return !hasError() && isOK();
}
public String reboot() throws IOException {
String rpc = "<rpc>" +
"<request-reboot/>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
return getRpcReply(rpc);
}
public String runCliCommand(String command) throws IOException, SAXException {
String rpc = "<rpc>" +
"<command format=\"text\">" +
command +
"</command>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
String rpcReply = getRpcReply(rpc);
lastRpcReply = rpcReply;
XML xmlReply = convertToXML(rpcReply);
List<String> tags = new ArrayList<>();
tags.add("output");
String output = xmlReply.findValue(tags);
if (output != null)
return output;
else
return rpcReply;
}
public BufferedReader runCliCommandRunning(String command) throws
IOException {
String rpc = "<command format=\"text\">" +
command +
"</command>";
return executeRPCRunning(rpc);
}
public void openConfiguration(String mode) throws IOException {
StringBuilder rpc = new StringBuilder();
rpc.append("<rpc>");
rpc.append("<open-configuration>");
if (mode.startsWith("<"))
rpc.append(mode);
else
rpc.append("<").append(mode).append("/>");
rpc.append("</open-configuration>");
rpc.append("</rpc>");
rpc.append(NetconfConstants.DEVICE_PROMPT);
lastRpcReply = getRpcReply(rpc.toString());
}
public void closeConfiguration() throws IOException {
String rpc = "<rpc>" +
"<close-configuration/>" +
"</rpc>" +
NetconfConstants.DEVICE_PROMPT;
lastRpcReply = getRpcReply(rpc);
}
public String getLastRPCReply() {
return this.lastRpcReply;
}
}
创建一个NetconfConstans类表示netconfig命令需要用到的一些公共部分。
package com.example.demo.netconfig;
public class NetconfConstants {
public static final String DEVICE_PROMPT = "]]>]]>";
public static final String XML_VERSION = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
public static final String URN_XML_NS_NETCONF_BASE_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0";
public static final String URN_IETF_PARAMS_NETCONF_BASE_1_0 = "urn:ietf:params:netconf:base:1.0";
}
创建一个XML类接收设备返回的XML消息。
package com.example.demo.netconfig;
import com.google.common.base.Preconditions;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class XML {
private final Element activeElement;
private final Document ownerDoc;
protected XML(Element active) {
this.activeElement = active;
ownerDoc = active.getOwnerDocument();
}
private String trim(String str) {
String st = str.trim();
if (st.startsWith("\n"))
st = st.substring(st.indexOf("\n") +1);
if (st.endsWith("\n")) {
st = st.substring(0,st.lastIndexOf("\n"));
}
return st;
}
public Document getOwnerDocument() {
return this.ownerDoc;
}
public XML append(String element) {
Element newElement = ownerDoc.createElement(element);
activeElement.appendChild(newElement);
return new XML(newElement);
}
public XML append(String element, String text) {
Element newElement = ownerDoc.createElement(element);
Node textNode = ownerDoc.createTextNode(text);
newElement.appendChild(textNode);
activeElement.appendChild(newElement);
return new XML(newElement);
}
public void append(String element, String[] text) {
for (String s : text) {
Element newElement = ownerDoc.createElement(element);
Node textNode = ownerDoc.createTextNode(s);
newElement.appendChild(textNode);
activeElement.appendChild(newElement);
}
}
public void append(Map<String, String> map) {
List<String> keyList = new ArrayList<>(map.keySet());
for (Object element : keyList) {
String elementName = (String) element;
String text = map.get(element);
Element newElement = ownerDoc.createElement(elementName);
Node textNode = ownerDoc.createTextNode(text);
newElement.appendChild(textNode);
activeElement.appendChild(newElement);
}
}
public XML append(String element, Map<String, String> map) {
Element newElement = ownerDoc.createElement(element);
activeElement.appendChild(newElement);
XML newXML = new XML(newElement);
newXML.append(map);
return newXML;
}
public void addSibling(String element) {
Element newElement = ownerDoc.createElement(element);
Node parentNode = activeElement.getParentNode();
parentNode.appendChild(newElement);
}
public void addSibling(String element, String text) {
Element newElement = ownerDoc.createElement(element);
Node textNode = ownerDoc.createTextNode(text);
newElement.appendChild(textNode);
Node parentNode = activeElement.getParentNode();
parentNode.appendChild(newElement);
}
public void addSiblings(String element, String[] text) {
Node parentNode = activeElement.getParentNode();
for (String s : text) {
Element newElement = ownerDoc.createElement(element);
Node textNode = ownerDoc.createTextNode(s);
newElement.appendChild(textNode);
parentNode.appendChild(newElement);
}
}
public void addSiblings(Map<String, String> map) {
List<String> keyList = new ArrayList<>(map.keySet());
activeElement.getParentNode();
for (Object element : keyList) {
String elementName = (String) element;
String text = map.get(element);
Element newElement = ownerDoc.createElement(elementName);
Node textNode = ownerDoc.createTextNode(text);
newElement.appendChild(textNode);
activeElement.appendChild(newElement); }
}
public XML addPath(String path) {
String[] elements = path.split("/");
Preconditions.checkArgument(elements.length >= 1);
Element newElement = null;
for (String element : elements) {
newElement = ownerDoc.createElement(element);
activeElement.appendChild(newElement);
}
return new XML(Objects.requireNonNull(newElement));
}
public void setAttribute(String name, String value) {
activeElement.setAttribute(name, value);
}
public void setText(String text) {
Node firstChild = activeElement.getFirstChild();
if (firstChild == null || firstChild.getNodeType() != Node.TEXT_NODE) {
Node textNode = ownerDoc.createTextNode(text);
activeElement.appendChild(textNode);
} else {
firstChild.setNodeValue(text);
}
}
public void junosDelete() {
activeElement.setAttribute("delete", "delete");
}
public void junosActivate() {
activeElement.setAttribute("active", "active");
}
public void junosDeactivate() {
activeElement.setAttribute("inactive", "inactive");
}
public void junosRename(String toBeRenamed, String newName) {
activeElement.setAttribute("rename", toBeRenamed);
activeElement.setAttribute("name", newName);
}
public void junosInsert(String before, String name) {
activeElement.setAttribute("insert", before);
activeElement.setAttribute("name", name);
}
public String findValue(List<String> list) {
Element nextElement = ownerDoc.getDocumentElement();
boolean nextElementFound;
for (int k=0; k<list.size(); k++) {
nextElementFound = false;
String nextElementName = list.get(k);
if (!nextElementName.contains("~")){
try {
NodeList nextElementList = nextElement.
getElementsByTagName(nextElementName);
String n2nString = null;
if (k<list.size()-1)
n2nString = list.get(k+1);
if (n2nString != null && n2nString.contains("~")) {
String n2nText = n2nString.substring(n2nString.
indexOf("~") + 1);
String n2nElementName = n2nString.substring(0,
n2nString.indexOf("~"));
for (int i=0; i<nextElementList.getLength(); i++) {
nextElement = (Element)nextElementList.item(i);
Element n2nElement = (Element)nextElement.
getElementsByTagName(n2nElementName).item(0);
String text = n2nElement.getFirstChild().
getNodeValue();
text = trim(text);
if (text != null && text.equals(n2nText)) {
nextElementFound = true;
break;
}
}
if (!nextElementFound)
return null;
} else {
nextElement = (Element)nextElementList.item(0);
}
} catch(NullPointerException e) {
return null;
}
} }
if (nextElement == null) {
return null;
}
String value = nextElement.getFirstChild().getNodeValue();
if (value == null) {
return null;
}
return trim(value);
}
public List<Node> findNodes(List<String> list) {
Element nextElement = ownerDoc.getDocumentElement();
boolean nextElementFound;
List<Node> finalList = new ArrayList<>();
for (int k=0; k<list.size(); k++) {
nextElementFound = false;
String nextElementName = list.get(k);
if (!nextElementName.contains("~")) {
try {
NodeList nextElementList = nextElement.
getElementsByTagName(nextElementName);
String n2nString = null;
if (k<list.size()-1)
n2nString = list.get(k+1);
if (n2nString != null && n2nString.contains("~")) {
String n2nText = n2nString.substring(n2nString.
indexOf("~") + 1);
String n2nElementName = n2nString.substring(0,
n2nString.indexOf("~"));
for (int i=0; i<nextElementList.getLength(); i++) {
nextElement = (Element)nextElementList.item(i);
Element n2nElement = (Element)nextElement.
getElementsByTagName(n2nElementName).item(0);
String text = n2nElement.getFirstChild().
getNodeValue();
text = trim(text);
if (text != null && text.equals(n2nText)) {
nextElementFound = true;
break;
}
}
if (!nextElementFound)
return null;
} else {
nextElement = (Element)nextElementList.item(0);
}
} catch(NullPointerException e) {
return null;
}
}
}
if (nextElement == null) {
return null;
}
String nodeName = nextElement.getNodeName();
String listLastEntry = list.get(list.size()-1);
if (listLastEntry.contains("~")) {
finalList.add(nextElement);
return finalList;
} else {
Element parent = (Element)nextElement.getParentNode();
NodeList nodeList = parent.getElementsByTagName(nodeName);
for (int i=0; i<nodeList.getLength(); i++) {
finalList.add(nodeList.item(i));
}
return finalList;
}
}
public String toString() {
String str;
try {
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
StringWriter buffer = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty
("{http://xml.apache.org/xslt}indent-amount", "4");
Element root = ownerDoc.getDocumentElement();
transformer.transform(new DOMSource(root), new StreamResult(buffer));
str = buffer.toString();
} catch (TransformerException ex) {
str = "Could not transform: Transformer exception";
}
return str;
}
}
最后创建一个NetconfigUtil类,进行连接并发送命令测试。
package com.example.demo.netconfig;
import cn.hutool.core.util.StrUtil;
import org.dom4j.DocumentException;
import org.xml.sax.SAXException;
import java.io.IOException;
public class NetconfUtils {
public static String getDevXML(String ip, String userName, String password, String msg) throws IOException, SAXException,RuntimeException{
if (StrUtil.isEmpty(ip) || StrUtil.isEmpty(userName) || StrUtil.isEmpty(password) || StrUtil.isEmpty(msg))
throw new RuntimeException(" |--用户名 或 密码 或 IP 或下发的数据为空 ");
String rpc_reply = null;
try {
Device device = CreateDevice.createDevice(ip, userName, password);
device.connect();
rpc_reply = device.executeRPC(msg);
} catch (RuntimeException e) {
e.printStackTrace();
}
// device.close();
return rpc_reply;
}
public static void main(String args[]) throws IOException, SAXException, DocumentException {
String message = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<get>\n" +
" <filter>\n" +
" <kylandDev xmlns=\"urn:kyland:parpms:xml:ns:yang:kyland-dev\"></kylandDev>\n" +
" </filter>\n" +
"</get>";
String msg = "\t<get>\n" +
"\n" +
"\t\t<filter>\n" +
"\t\t\t<kylandLldp xmlns=\"urn:kyland:parpms:xml:ns:yang:kyland-lldp\"></kylandLldp>\n" +
"\t\t</filter>\n" +
"</get>";
String devXML = NetconfUtils.getDevXML("192.168.100.15", "admin", "123", message);
System.out.println("****************");
System.out.println(devXML);
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)