From 4ff2893ffcbf3d618450770698874881ee931fc7 Mon Sep 17 00:00:00 2001 From: Devdeep Singh Date: Tue, 8 May 2012 17:31:36 +0530 Subject: [PATCH] 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). --- .../utils/cisco/n1kv/vsm/NetconfHelper.java | 261 ++------------ .../utils/cisco/n1kv/vsm/VsmCommand.java | 330 ++++++++++++++++++ .../utils/cisco/n1kv/vsm/VsmResponse.java | 10 +- 3 files changed, 368 insertions(+), 233 deletions(-) create mode 100644 utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmCommand.java diff --git a/utils/src/com/cloud/utils/cisco/n1kv/vsm/NetconfHelper.java b/utils/src/com/cloud/utils/cisco/n1kv/vsm/NetconfHelper.java index aee4665209f..742226c2bb8 100644 --- a/utils/src/com/cloud/utils/cisco/n1kv/vsm/NetconfHelper.java +++ b/utils/src/com/cloud/utils/cisco/n1kv/vsm/NetconfHelper.java @@ -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 = "" + "" + " " @@ -66,231 +58,29 @@ public class NetconfHelper { + " " + " " + " " + " " + "" + 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 - 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()); + } + } } diff --git a/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmCommand.java b/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmCommand.java new file mode 100644 index 00000000000..064762f8528 --- /dev/null +++ b/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmCommand.java @@ -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); + } +} diff --git a/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmResponse.java b/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmResponse.java index 6bc6371fdb4..50db9981e52 100644 --- a/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmResponse.java +++ b/utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmResponse.java @@ -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();