mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
CS-9919: Support for Nexus Swiches (Cisco Vswitches)
Description: Updated the rpc request generation logic for creating and deleting port profiles.
This commit is contained in:
parent
ab957b8928
commit
631cc863e1
@ -5,6 +5,17 @@ import org.apache.log4j.Logger;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
import org.w3c.dom.DOMException;
|
||||
|
||||
import com.cloud.utils.ssh.*;
|
||||
import com.trilead.ssh2.Session;
|
||||
import com.trilead.ssh2.Connection;
|
||||
@ -12,205 +23,295 @@ import com.trilead.ssh2.ChannelCondition;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public class NetconfHelper {
|
||||
private static final Logger s_logger = Logger.getLogger(NetconfHelper.class);
|
||||
private static final Logger s_logger = Logger.getLogger(NetconfHelper.class);
|
||||
|
||||
private static final String SSH_NETCONF_TERMINATOR = "]]>]]>";
|
||||
private static final String SSH_NETCONF_TERMINATOR = "]]>]]>";
|
||||
|
||||
private Connection _connection;
|
||||
private Connection _connection;
|
||||
|
||||
private Session _session;
|
||||
private Session _session;
|
||||
|
||||
public NetconfHelper (String ip, String username, String password) throws CloudRuntimeException {
|
||||
_connection = SSHCmdHelper.acquireAuthorizedConnection(ip, username, password);
|
||||
if (_connection == null) {
|
||||
throw new CloudRuntimeException("Error opening ssh connection.");
|
||||
}
|
||||
public NetconfHelper(String ip, String username, String password) throws CloudRuntimeException {
|
||||
_connection = SSHCmdHelper.acquireAuthorizedConnection(ip, username, password);
|
||||
if (_connection == null) {
|
||||
throw new CloudRuntimeException("Error opening ssh connection.");
|
||||
}
|
||||
|
||||
try {
|
||||
_session = _connection.openSession();
|
||||
_session.startSubSystem("xmlagent");
|
||||
exchangeHello();
|
||||
} catch (final Exception e) {
|
||||
disconnect();
|
||||
s_logger.error("Failed to connect to device SSH server: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to connect to SSH server: " + _connection.getHostname());
|
||||
}
|
||||
}
|
||||
try {
|
||||
_session = _connection.openSession();
|
||||
_session.startSubSystem("xmlagent");
|
||||
exchangeHello();
|
||||
} catch (final Exception e) {
|
||||
disconnect();
|
||||
s_logger.error("Failed to connect to device SSH server: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to connect to SSH server: "
|
||||
+ _connection.getHostname());
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (_session != null) {
|
||||
_session.close();
|
||||
}
|
||||
SSHCmdHelper.releaseSshConnection(_connection);
|
||||
}
|
||||
public void disconnect() {
|
||||
if (_session != null) {
|
||||
_session.close();
|
||||
}
|
||||
SSHCmdHelper.releaseSshConnection(_connection);
|
||||
}
|
||||
|
||||
public void queryStatus() {
|
||||
// FIXME: Use an xml parser to generate this request.
|
||||
String status = "<?xml version=\"1.0\"?>" +
|
||||
"<nc:rpc message-id=\"1\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0" +
|
||||
"\"xmlns=\"http://www.cisco.com/nxos:1.0:xml\">" +
|
||||
" <nc:get>" +
|
||||
" <nc:filter type=\"subtree\">" +
|
||||
" <show>" +
|
||||
" <xml>" +
|
||||
" <server>" +
|
||||
" <status/>" +
|
||||
" </server>" +
|
||||
" </xml>" +
|
||||
" </show>" +
|
||||
" </nc:filter>" +
|
||||
" </nc:get>" +
|
||||
"</nc:rpc>" +
|
||||
SSH_NETCONF_TERMINATOR;
|
||||
send(status);
|
||||
String reply = receive();
|
||||
}
|
||||
public void queryStatus() {
|
||||
// This command is used query the server status.
|
||||
String status = "<?xml version=\"1.0\"?>"
|
||||
+ "<nc:rpc message-id=\"1\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0"
|
||||
+ "\"xmlns=\"http://www.cisco.com/nxos:1.0:xml\">" + " <nc:get>"
|
||||
+ " <nc:filter type=\"subtree\">" + " <show>" + " <xml>"
|
||||
+ " <server>" + " <status/>" + " </server>"
|
||||
+ " </xml>" + " </show>" + " </nc:filter>" + " </nc:get>"
|
||||
+ "</nc:rpc>" + SSH_NETCONF_TERMINATOR;
|
||||
send(status);
|
||||
String reply = receive();
|
||||
}
|
||||
|
||||
public boolean addPortProfile(String name, int vlan) {
|
||||
// FIXME: Use an xml parser to generate this request.
|
||||
String command = "<?xml version=\"1.0\"?>" +
|
||||
"<nf:rpc xmlns=\"http://www.cisco.com/nxos:1.0:ppm\" " +
|
||||
"xmlns:nf=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"110\">" +
|
||||
" <nf:edit-config>" +
|
||||
" <nf:target>" +
|
||||
" <nf:running/>" +
|
||||
" </nf:target>" +
|
||||
" <nf:config>" +
|
||||
" <nxos:configure xmlns:nxos=\"http://www.cisco.com/nxos:1.0:ppm\">" +
|
||||
" <nxos:__XML__MODE__exec_configure>" +
|
||||
" <port-profile>" +
|
||||
" <type>" +
|
||||
" <vethernet>" +
|
||||
" <name>" +
|
||||
" <__XML__PARAM_value isKey=\"true\">@name</__XML__PARAM_value>" +
|
||||
" <__XML__MODE_port-prof>" +
|
||||
" <switchport>" +
|
||||
" <mode>" +
|
||||
" <access/>" +
|
||||
" </mode>" +
|
||||
" </switchport>" +
|
||||
" <switchport>" +
|
||||
" <access>" +
|
||||
" <vlan>" +
|
||||
" <vlan-id-create-delete>" +
|
||||
" <__XML__PARAM_value>@vlan</__XML__PARAM_value>" +
|
||||
" </vlan-id-create-delete>" +
|
||||
" </vlan>" +
|
||||
" </access>" +
|
||||
" </switchport>" +
|
||||
" <vmware>" +
|
||||
" <port-group/>" +
|
||||
" </vmware>" +
|
||||
" <state>" +
|
||||
" <enabled/>" +
|
||||
" </state>" +
|
||||
" <no>" +
|
||||
" <shutdown/>" +
|
||||
" </no>" +
|
||||
" </__XML__MODE_port-prof>" +
|
||||
" </name>" +
|
||||
" </vethernet>" +
|
||||
" </type>" +
|
||||
" </port-profile>" +
|
||||
" </nxos:__XML__MODE__exec_configure>" +
|
||||
" </nxos:configure>" +
|
||||
" </nf:config>" +
|
||||
" </nf:edit-config>" +
|
||||
"</nf:rpc>" +
|
||||
SSH_NETCONF_TERMINATOR;
|
||||
command = command.replace("@name", name);
|
||||
command = command.replace("@vlan", Integer.toString(vlan));
|
||||
send(command);
|
||||
// parse the rpc reply and the return success or failure.
|
||||
String reply = receive();
|
||||
return true;
|
||||
}
|
||||
private String getAddPortProfile(String portName, int vlanid) {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = docBuilder.getDOMImplementation();
|
||||
|
||||
public boolean deletePortProfile(String name) {
|
||||
// FIXME: Use an xml parser to generate this request.
|
||||
String command = "<?xml version=\"1.0\"?>" +
|
||||
"<nf:rpc xmlns=\"http://www.cisco.com/nxos:1.0:ppm\" " +
|
||||
"xmlns:nf=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"110\">" +
|
||||
" <nf:edit-config>" +
|
||||
" <nf:target>" +
|
||||
" <nf:running/>" +
|
||||
" </nf:target>" +
|
||||
" <nf:config>" +
|
||||
" <nxos:configure xmlns:nxos=\"http://www.cisco.com/nxos:1.0:ppm\">" +
|
||||
" <nxos:__XML__MODE__exec_configure>" +
|
||||
" <no>" +
|
||||
" <port-profile>" +
|
||||
" <name>" +
|
||||
" <__XML__PARAM_value isKey=\"true\">@name</__XML__PARAM_value>" +
|
||||
" </name>" +
|
||||
" </port-profile>" +
|
||||
" </no>" +
|
||||
" </nxos:__XML__MODE__exec_configure>" +
|
||||
" </nxos:configure>" +
|
||||
" </nf:config>" +
|
||||
" </nf:edit-config>" +
|
||||
"</nf:rpc>" +
|
||||
SSH_NETCONF_TERMINATOR;
|
||||
command = command.replace("@name", name);
|
||||
send(command);
|
||||
// parse the rpc reply and the return success or failure.
|
||||
String reply = receive();
|
||||
return true;
|
||||
}
|
||||
// Root elements.
|
||||
Document doc = domImpl.createDocument("urn:ietf:params:xml:ns:netconf:base:1.0",
|
||||
"nf:rpc", null);
|
||||
doc.getDocumentElement().setAttribute( "message-id", "101" );
|
||||
doc.getDocumentElement().setAttributeNS("http://www.cisco.com/nxos:1.0:ppm",
|
||||
"addportprofile", "true");
|
||||
|
||||
private void exchangeHello() {
|
||||
String ack = receive();
|
||||
String hello = "<?xml version=\"1.0\"?>" +
|
||||
"<nc:hello xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" +
|
||||
" <nc:capabilities>" +
|
||||
" <nc:capability>urn:ietf:params:xml:ns:netconf:base:1.0</nc:capability>" +
|
||||
" </nc:capabilities>" +
|
||||
"</nc:hello>" +
|
||||
SSH_NETCONF_TERMINATOR;
|
||||
send(hello);
|
||||
}
|
||||
// Edit configuration command.
|
||||
Element editConfig = doc.createElement("nf:edit-config");
|
||||
Element target = doc.createElement("nf:target");
|
||||
Element running = doc.createElement("nf:running");
|
||||
target.appendChild(running);
|
||||
Element config = doc.createElement("nf:config");
|
||||
Element configure = doc.createElementNS("http://www.cisco.com/nxos:1.0:ppm", "nxos:configure");
|
||||
Element execConfigure = doc.createElement("nxos:__XML__MODE__exec_configure");
|
||||
Element portProfile = doc.createElement("port-profile");
|
||||
Element type = doc.createElement("type");
|
||||
Element ethernettype = doc.createElement("vethernet");
|
||||
Element name = doc.createElement("name");
|
||||
Element value = doc.createElement("__XML__PARAM_value");
|
||||
value.setAttribute("isKey", "true");
|
||||
value.setTextContent(portName);
|
||||
|
||||
private void send(String message) {
|
||||
try {
|
||||
OutputStream outputStream = _session.getStdin();
|
||||
outputStream.write(message.getBytes());
|
||||
outputStream.flush();
|
||||
} catch(Exception e) {
|
||||
s_logger.error("Failed to send message: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to send message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
// Port profile details start here.
|
||||
Element portProf = doc.createElement("__XML__MODE__port-prof");
|
||||
|
||||
private String receive() {
|
||||
byte[] buffer = new byte[8192];
|
||||
InputStream inputStream = _session.getStdout();
|
||||
try {
|
||||
while (true) {
|
||||
if (inputStream.available() == 0) {
|
||||
int conditions = _session.waitForCondition(ChannelCondition.STDOUT_DATA |
|
||||
ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 500);
|
||||
// Command : switchport mode access
|
||||
Element switchport1 = doc.createElement("switchport");
|
||||
Element mode1 = doc.createElement("mode");
|
||||
Element access1 = doc.createElement("access");
|
||||
mode1.appendChild(access1);
|
||||
switchport1.appendChild(mode1);
|
||||
|
||||
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
|
||||
break;
|
||||
}
|
||||
// Command : switchport access vlan <vlanid>
|
||||
Element switchport2 = doc.createElement("switchport");
|
||||
Element access2 = doc.createElement("access");
|
||||
Element vlan = doc.createElement("vlan");
|
||||
Element vlancreate = doc.createElement("vlan-id-create-delete");
|
||||
Element value2 = doc.createElement("__XML__PARAM_value");
|
||||
value2.setTextContent(Integer.toString(vlanid));
|
||||
vlancreate.appendChild(value2);
|
||||
vlan.appendChild(vlancreate);
|
||||
access2.appendChild(vlan);
|
||||
switchport2.appendChild(access2);
|
||||
|
||||
if ((conditions & ChannelCondition.EOF) != 0) {
|
||||
if ((conditions & (ChannelCondition.STDOUT_DATA |
|
||||
ChannelCondition.STDERR_DATA)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Command : vmware port-group
|
||||
Element vmware1 = doc.createElement("vmware");
|
||||
Element portgroup = doc.createElement("port-group");
|
||||
vmware1.appendChild(portgroup);
|
||||
|
||||
while (inputStream.available() > 0) {
|
||||
inputStream.read(buffer);
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
s_logger.error("Failed to receive message: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to receives message: " + e.getMessage());
|
||||
}
|
||||
// Command : vmware state enabled.
|
||||
Element vmware2 = doc.createElement("vmware");
|
||||
Element state = doc.createElement("state");
|
||||
Element enabled = doc.createElement("enabled");
|
||||
state.appendChild(enabled);
|
||||
vmware2.appendChild(state);
|
||||
|
||||
return new String(buffer);
|
||||
}
|
||||
// Command : no shutdown.
|
||||
Element no = doc.createElement("no");
|
||||
Element shutdown = doc.createElement("shutdown");
|
||||
no.appendChild(shutdown);
|
||||
|
||||
// Put the port profile details together.
|
||||
portProf.appendChild(switchport1);
|
||||
portProf.appendChild(switchport2);
|
||||
portProf.appendChild(vmware1);
|
||||
portProf.appendChild(vmware2);
|
||||
portProf.appendChild(no);
|
||||
|
||||
// Put the xml-rpc together.
|
||||
name.appendChild(value);
|
||||
name.appendChild(portProf);
|
||||
ethernettype.appendChild(name);
|
||||
type.appendChild(ethernettype);
|
||||
portProfile.appendChild(type);
|
||||
execConfigure.appendChild(portProfile);
|
||||
configure.appendChild(execConfigure);
|
||||
config.appendChild(configure);
|
||||
editConfig.appendChild(target);
|
||||
editConfig.appendChild(config);
|
||||
doc.getDocumentElement().appendChild(editConfig);
|
||||
|
||||
return serialize(domImpl, doc);
|
||||
} catch (ParserConfigurationException e) {
|
||||
s_logger.error("Error while creating add port profile message : " + e.getMessage());
|
||||
return null;
|
||||
} catch (DOMException e) {
|
||||
s_logger.error("Error while creating add port profile message : " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addPortProfile(String name, int vlan) {
|
||||
String command = getAddPortProfile(name, vlan) + SSH_NETCONF_TERMINATOR;
|
||||
send(command);
|
||||
// parse the rpc reply and the return success or failure.
|
||||
String reply = receive();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getDeletePortProfile(String portName) {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = docBuilder.getDOMImplementation();
|
||||
|
||||
// Root elements.
|
||||
Document doc = domImpl.createDocument("urn:ietf:params:xml:ns:netconf:base:1.0",
|
||||
"nf:rpc", null);
|
||||
doc.getDocumentElement().setAttribute( "message-id", "101" );
|
||||
doc.getDocumentElement().setAttributeNS("http://www.cisco.com/nxos:1.0:ppm",
|
||||
"deleteportprofile", "true");
|
||||
|
||||
// Edit configuration command.
|
||||
Element editConfig = doc.createElement("nf:edit-config");
|
||||
|
||||
// Details of the port profile to delete.
|
||||
Element target = doc.createElement("nf:target");
|
||||
Element running = doc.createElement("nf:running");
|
||||
target.appendChild(running);
|
||||
|
||||
Element config = doc.createElement("nf:config");
|
||||
Element configure = doc.createElementNS("http://www.cisco.com/nxos:1.0:ppm", "nxos:configure");
|
||||
Element execConfigure = doc.createElement("nxos:__XML__MODE__exec_configure");
|
||||
Element delete = doc.createElement("no");
|
||||
Element portProfile = doc.createElement("port-profile");
|
||||
Element name = doc.createElement("name");
|
||||
Element value = doc.createElement("__XML__PARAM_value");
|
||||
value.setAttribute("isKey", "true");
|
||||
value.setTextContent(portName);
|
||||
|
||||
// Put the xml-rpc together.
|
||||
name.appendChild(value);
|
||||
portProfile.appendChild(name);
|
||||
delete.appendChild(portProfile);
|
||||
execConfigure.appendChild(delete);
|
||||
configure.appendChild(execConfigure);
|
||||
config.appendChild(configure);
|
||||
editConfig.appendChild(target);
|
||||
editConfig.appendChild(config);
|
||||
doc.getDocumentElement().appendChild(editConfig);
|
||||
|
||||
return serialize(domImpl, doc);
|
||||
} catch (ParserConfigurationException e) {
|
||||
s_logger.error("Error while creating delete message : " + e.getMessage());
|
||||
return null;
|
||||
} catch (DOMException e) {
|
||||
s_logger.error("Error while creating delete message : " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deletePortProfile(String name) {
|
||||
String command = getDeletePortProfile(name) + SSH_NETCONF_TERMINATOR;
|
||||
send(command);
|
||||
// parse the rpc reply and the return success or failure.
|
||||
String reply = receive();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getHello() {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = docBuilder.getDOMImplementation();
|
||||
|
||||
// Root elements.
|
||||
Document doc = domImpl.createDocument("urn:ietf:params:xml:ns:netconf:base:1.0",
|
||||
"nc:hello", null);
|
||||
|
||||
// Client capacity. We are only supporting basic capacity.
|
||||
Element capabilities = doc.createElement("nc:capabilities");
|
||||
Element capability = doc.createElement("nc:capability");
|
||||
capability.setTextContent("urn:ietf:params:xml:ns:netconf:base:1.0");
|
||||
|
||||
capabilities.appendChild(capability);
|
||||
doc.getDocumentElement().appendChild(capabilities);
|
||||
|
||||
return serialize(domImpl, doc);
|
||||
} catch (ParserConfigurationException e) {
|
||||
s_logger.error("Error while creating hello message : " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String serialize(DOMImplementation domImpl, Document document) {
|
||||
DOMImplementationLS ls = (DOMImplementationLS) domImpl;
|
||||
LSSerializer lss = ls.createLSSerializer();
|
||||
return lss.writeToString(document);
|
||||
}
|
||||
|
||||
private void exchangeHello() {
|
||||
String ack = receive();
|
||||
String hello = getHello() + SSH_NETCONF_TERMINATOR;
|
||||
send(hello);
|
||||
}
|
||||
|
||||
private void send(String message) {
|
||||
try {
|
||||
OutputStream outputStream = _session.getStdin();
|
||||
outputStream.write(message.getBytes());
|
||||
outputStream.flush();
|
||||
} catch (Exception e) {
|
||||
s_logger.error("Failed to send message: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to send message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String receive() {
|
||||
byte[] buffer = new byte[8192];
|
||||
InputStream inputStream = _session.getStdout();
|
||||
try {
|
||||
while (true) {
|
||||
if (inputStream.available() == 0) {
|
||||
int conditions = _session.waitForCondition(ChannelCondition.STDOUT_DATA
|
||||
| ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 100);
|
||||
|
||||
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((conditions & ChannelCondition.EOF) != 0) {
|
||||
if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (inputStream.available() > 0) {
|
||||
inputStream.read(buffer);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.error("Failed to receive message: " + e.getMessage());
|
||||
throw new CloudRuntimeException("Failed to receives message: " + e.getMessage());
|
||||
}
|
||||
|
||||
return new String(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user