CS-9919: Support for Nexus Swiches (Cisco Vswitches)

Description:

	Updated the add port profile routine to support
	more options (bindingtype, switchport mode, port
	profile types).
This commit is contained in:
Devdeep Singh 2012-05-08 17:31:36 +05:30 committed by Vijayendra Bhamidipati
parent 0f28222cce
commit 4ff2893ffc
3 changed files with 368 additions and 233 deletions

View File

@ -5,21 +5,13 @@ 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;
import com.trilead.ssh2.ChannelCondition;
import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.BindingType;
import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.PortProfileType;
import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.SwitchPortMode;
import com.cloud.utils.exception.CloudRuntimeException;
public class NetconfHelper {
@ -56,8 +48,8 @@ public class NetconfHelper {
SSHCmdHelper.releaseSshConnection(_connection);
}
public void queryStatus() {
// This command is used query the server status.
public void queryStatus() throws CloudRuntimeException {
// This command is used to 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>"
@ -66,231 +58,29 @@ public class NetconfHelper {
+ " </xml>" + " </show>" + " </nc:filter>" + " </nc:get>"
+ "</nc:rpc>" + SSH_NETCONF_TERMINATOR;
send(status);
String reply = receive();
}
private String getAddPortProfile(String portName, int vlanid) {
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",
"addportprofile", "true");
// 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);
// Port profile details start here.
Element portProf = doc.createElement("__XML__MODE__port-prof");
// 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);
// 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);
// Command : vmware port-group
Element vmware = doc.createElement("vmware");
Element portgroup = doc.createElement("port-group");
vmware.appendChild(portgroup);
// Command : state enabled.
Element state = doc.createElement("state");
Element enabled = doc.createElement("enabled");
state.appendChild(enabled);
// 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(vmware);
portProf.appendChild(state);
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 void 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().trim();
if (reply.endsWith(SSH_NETCONF_TERMINATOR)) {
reply = reply.substring(0, reply.length() - (new String(SSH_NETCONF_TERMINATOR).length()));
}
else {
throw new CloudRuntimeException("Malformed repsonse from vsm for add " +
"port profile request: " + reply);
}
VsmResponse response = new VsmResponse(reply);
if (!response.isResponseOk()) {
throw new CloudRuntimeException(response.toString());
}
parseReply(receive());
}
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 void deletePortProfile(String name) {
String command = getDeletePortProfile(name) + SSH_NETCONF_TERMINATOR;
public void addPortProfile(String name, PortProfileType type, BindingType binding,
SwitchPortMode mode, int vlanid) throws CloudRuntimeException {
String command = VsmCommand.getAddPortProfile(name, type, binding, mode, vlanid);
command = command.concat(SSH_NETCONF_TERMINATOR);
send(command);
// parse the rpc reply and the return success or failure.
String reply = receive().trim();
if (reply.endsWith(SSH_NETCONF_TERMINATOR)) {
reply = reply.substring(0, reply.length() - (new String(SSH_NETCONF_TERMINATOR).length()));
}
else {
throw new CloudRuntimeException("Malformed repsonse from vsm for delete " +
"port profile request :" + reply);
}
VsmResponse response = new VsmResponse(reply);
if (!response.isResponseOk()) {
throw new CloudRuntimeException(response.toString());
}
parseReply(receive());
}
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);
public void deletePortProfile(String name) throws CloudRuntimeException {
String command = VsmCommand.getDeletePortProfile(name) + SSH_NETCONF_TERMINATOR;
send(command);
// parse the rpc reply and the return success or failure.
parseReply(receive());
}
private void exchangeHello() {
String ack = receive();
String hello = getHello() + SSH_NETCONF_TERMINATOR;
String hello = VsmCommand.getHello() + SSH_NETCONF_TERMINATOR;
send(hello);
}
@ -312,7 +102,7 @@ public class NetconfHelper {
while (true) {
if (inputStream.available() == 0) {
int conditions = _session.waitForCondition(ChannelCondition.STDOUT_DATA
| ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 100);
| ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 500);
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
break;
@ -336,4 +126,19 @@ public class NetconfHelper {
return new String(buffer);
}
private void parseReply(String reply) throws CloudRuntimeException {
reply = reply.trim();
if (reply.endsWith(SSH_NETCONF_TERMINATOR)) {
reply = reply.substring(0, reply.length() - (new String(SSH_NETCONF_TERMINATOR).length()));
}
else {
throw new CloudRuntimeException("Malformed response from vsm" + reply);
}
VsmResponse response = new VsmResponse(reply);
if (!response.isResponseOk()) {
throw new CloudRuntimeException(response.toString());
}
}
}

View File

@ -0,0 +1,330 @@
package com.cloud.utils.cisco.n1kv.vsm;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.w3c.dom.DOMException;
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;
public class VsmCommand {
private static final Logger s_logger = Logger.getLogger(VsmCommand.class);
private static final String s_namespace = "urn:ietf:params:xml:ns:netconf:base:1.0";
private static final String s_ciscons = "http://www.cisco.com/nxos:1.0:ppm";
private static final String s_configuremode = "__XML__MODE__exec_configure";
private static final String s_portprofmode = "__XML__MODE_port-prof";
private static final String s_paramvalue = "__XML__PARAM_value";
public enum PortProfileType {
none,
vethernet,
ethernet;
}
public enum BindingType {
none,
portbindingstatic,
portbindingdynamic,
portbindingephermal;
}
public enum SwitchPortMode {
none,
access,
trunk,
privatevlanhost,
privatevlanpromiscuous
}
public static String getAddPortProfile(String name, PortProfileType type,
BindingType binding, SwitchPortMode mode, int vlanid) {
try {
// Create the document and root element.
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
DOMImplementation domImpl = docBuilder.getDOMImplementation();
Document doc = createDocument(domImpl);
// Edit configuration command.
Element editConfig = doc.createElement("nf:edit-config");
doc.getDocumentElement().appendChild(editConfig);
// Command to get into exec configure mode.
Element target = doc.createElement("nf:target");
Element running = doc.createElement("nf:running");
target.appendChild(running);
editConfig.appendChild(target);
// Command to create the port profile with the desired configuration.
Element config = doc.createElement("nf:config");
config.appendChild(configPortProfileDetails(doc, name, type, binding, mode, vlanid));
editConfig.appendChild(config);
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 static String getDeletePortProfile(String portName) {
try {
// Create the document and root element.
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
DOMImplementation domImpl = docBuilder.getDOMImplementation();
Document doc = createDocument(domImpl);
// Edit configuration command.
Element editConfig = doc.createElement("nf:edit-config");
doc.getDocumentElement().appendChild(editConfig);
// Command to get into exec configure mode.
Element target = doc.createElement("nf:target");
Element running = doc.createElement("nf:running");
target.appendChild(running);
editConfig.appendChild(target);
// Command to create the port profile with the desired configuration.
Element config = doc.createElement("nf:config");
config.appendChild(deletePortProfileDetails(doc, portName));
editConfig.appendChild(config);
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 static String getHello() {
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
DOMImplementation domImpl = docBuilder.getDOMImplementation();
// Root elements.
Document doc = domImpl.createDocument(s_namespace, "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;
} catch (DOMException e) {
s_logger.error("Error while creating delete message : " + e.getMessage());
return null;
}
}
private static Element configPortProfileDetails(Document doc, String name, PortProfileType type,
BindingType binding, SwitchPortMode mode, int vlanid) {
// In mode, exec_configure.
Element configure = doc.createElementNS(s_ciscons, "nxos:configure");
Element modeConfigure = doc.createElement("nxos:" + s_configuremode);
configure.appendChild(modeConfigure);
// Port profile name and type configuration.
Element portProfile = doc.createElement("port-profile");
modeConfigure.appendChild(portProfile);
// Port profile type.
Element portDetails = doc.createElement("name");
switch (type) {
case none:
portProfile.appendChild(portDetails);
break;
case ethernet:
{
Element typetag = doc.createElement("type");
Element ethernettype = doc.createElement("ethernet");
portProfile.appendChild(typetag);
typetag.appendChild(ethernettype);
ethernettype.appendChild(portDetails);
}
break;
case vethernet:
{
Element typetag = doc.createElement("type");
Element ethernettype = doc.createElement("vethernet");
portProfile.appendChild(typetag);
typetag.appendChild(ethernettype);
ethernettype.appendChild(portDetails);
}
break;
}
// Port profile name.
Element value = doc.createElement(s_paramvalue);
value.setAttribute("isKey", "true");
value.setTextContent(name);
portDetails.appendChild(value);
// element for port prof mode.
Element portProf = doc.createElement(s_portprofmode);
portDetails.appendChild(portProf);
// Binding type.
if (binding != BindingType.none) {
portProf.appendChild(getBindingType(doc, binding));
}
if (mode != SwitchPortMode.none) {
// Switchport mode.
portProf.appendChild(getSwitchPortMode(doc, mode));
// Adding vlan details.
portProf.appendChild(getVlanDetails(doc, mode, vlanid));
}
// Command "vmware port-group".
Element vmware = doc.createElement("vmware");
Element portgroup = doc.createElement("port-group");
vmware.appendChild(portgroup);
portProf.appendChild(vmware);
// no shutdown.
Element no = doc.createElement("no");
Element shutdown = doc.createElement("shutdown");
no.appendChild(shutdown);
portProf.appendChild(no);
// Enable the port profile.
Element state = doc.createElement("state");
Element enabled = doc.createElement("enabled");
state.appendChild(enabled);
portProf.appendChild(state);
// Persist the configuration across reboots.
// modeConfigure.appendChild(persistConfiguration(doc));
return configure;
}
private static Element deletePortProfileDetails(Document doc, String name) {
Element configure = doc.createElementNS(s_ciscons, "nxos:configure");
Element modeConfigure = doc.createElement("nxos:" + s_configuremode);
configure.appendChild(modeConfigure);
// Command and name for the port profile to be deleted.
Element deletePortProfile = doc.createElement("no");
modeConfigure.appendChild(deletePortProfile);
Element portProfile = doc.createElement("port-profile");
deletePortProfile.appendChild(portProfile);
Element portDetails = doc.createElement("name");
portProfile.appendChild(portDetails);
// Name of the profile to delete.
Element value = doc.createElement(s_paramvalue);
value.setAttribute("isKey", "true");
value.setTextContent(name);
portDetails.appendChild(value);
// Persist the configuration across reboots.
// modeConfigure.appendChild(persistConfiguration(doc));
return configure;
}
private static Element persistConfiguration(Document doc) {
Element copy = doc.createElement("copy");
Element running = doc.createElement("running-config");
Element startup = doc.createElement("startup-config");
copy.appendChild(running);
running.appendChild(startup);
return copy;
}
private static Element getVlanDetails(Document doc, SwitchPortMode mode, int vlanid) {
Element switchport = doc.createElement("switchport");
// Handling is there only for 'access' mode command.
if (mode == SwitchPortMode.access) {
Element access = doc.createElement("access");
switchport.appendChild(access);
Element vlan = doc.createElement("vlan");
access.appendChild(vlan);
Element vlancreate = doc.createElement("vlan-id-create-delete");
vlan.appendChild(vlancreate);
Element value = doc.createElement(s_paramvalue);
value.setTextContent(Integer.toString(vlanid));
vlancreate.appendChild(value);
}
return switchport;
}
private static Element getBindingType(Document doc, BindingType binding) {
Element portBinding = doc.createElement("port-binding");
// We only have handling for access or trunk mode. Handling for private-vlan
// host/promiscuous command will have to be added.
if (binding == BindingType.portbindingstatic) {
Element type = doc.createElement("static");
portBinding.appendChild(type);
} else if (binding == BindingType.portbindingdynamic) {
Element type = doc.createElement("dynamic");
portBinding.appendChild(type);
} else if (binding == BindingType.portbindingephermal) {
Element type = doc.createElement("ephemeral");
portBinding.appendChild(type);
}
return portBinding;
}
private static Element getSwitchPortMode(Document doc, SwitchPortMode mode) {
Element switchport = doc.createElement("switchport");
Element accessmode = doc.createElement("mode");
switchport.appendChild(accessmode);
// We only have handling for access or trunk mode. Handling for private-vlan
// host/promiscuous command will have to be added.
if (mode == SwitchPortMode.access) {
Element access = doc.createElement("access");
accessmode.appendChild(access);
} else if (mode == SwitchPortMode.trunk) {
Element trunk = doc.createElement("trunk");
accessmode.appendChild(trunk);
}
return switchport;
}
private static Document createDocument(DOMImplementation dom) {
Document doc = dom.createDocument(s_namespace, "nf:rpc", null);
doc.getDocumentElement().setAttribute( "message-id", "101" );
doc.getDocumentElement().setAttributeNS(s_ciscons, "portprofile", "true");
return doc;
}
private static String serialize(DOMImplementation domImpl, Document document) {
DOMImplementationLS ls = (DOMImplementationLS) domImpl;
LSSerializer lss = ls.createLSSerializer();
return lss.writeToString(document);
}
}

View File

@ -99,19 +99,19 @@ public class VsmResponse {
StringBuffer error = new StringBuffer("");
error.append(" Severity: " + _severity)
.append(" Error code: " + _tag)
.append(" Error type: " + _type);
.append(", Error code: " + _tag)
.append(", Error type: " + _type);
if (_message != null) {
error.append(" Error Message: " + _message);
error.append(", Error Message: " + _message);
}
if (_info != null) {
error.append(" Error info: " + _info);
error.append(", Error info: " + _info);
}
if (_path != null) {
error.append(" Path: " + _path);
error.append(", Path: " + _path);
}
return error.toString();