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:
Devdeep Singh 2012-05-06 13:09:16 +05:30 committed by Vijayendra Bhamidipati
parent ab957b8928
commit 631cc863e1

View File

@ -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);
}
}