mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
CS-14948: Fixing an issue with parsing the xml-rpc response to a command
from vsm. This was throwing false exceptions when infact the command execution was a success. Also adding retry logic for create port profile request.
This commit is contained in:
parent
a20aace44e
commit
3006bed6df
@ -12,7 +12,6 @@ import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.PortProfileType;
|
||||
import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.SwitchPortMode;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.ssh.SSHCmdHelper;
|
||||
import com.trilead.ssh2.ChannelCondition;
|
||||
import com.trilead.ssh2.Connection;
|
||||
import com.trilead.ssh2.Session;
|
||||
|
||||
@ -21,6 +20,9 @@ public class NetconfHelper {
|
||||
|
||||
private static final String SSH_NETCONF_TERMINATOR = "]]>]]>";
|
||||
|
||||
// Number of times to retry the command on failure.
|
||||
private static final int s_retryCount = 3;
|
||||
|
||||
private Connection _connection;
|
||||
|
||||
private Session _session;
|
||||
@ -61,7 +63,7 @@ public class NetconfHelper {
|
||||
+ "</nc:rpc>" + SSH_NETCONF_TERMINATOR;
|
||||
send(status);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
}
|
||||
|
||||
public void addPortProfile(String name, PortProfileType type, BindingType binding,
|
||||
@ -69,9 +71,28 @@ public class NetconfHelper {
|
||||
String command = VsmCommand.getAddPortProfile(name, type, binding, mode, vlanid);
|
||||
if (command != null) {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
|
||||
// This command occasionally fails. On retry it succeeds. Putting in
|
||||
// retry to handle failures.
|
||||
for (int i = 0; i < s_retryCount; ++i) {
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
// parseOkReply(receive());
|
||||
VsmOkResponse response = new VsmOkResponse(receive().trim());
|
||||
if (!response.isResponseOk()) {
|
||||
if (i >= s_retryCount) {
|
||||
throw new CloudRuntimeException(response.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (final InterruptedException e) {
|
||||
s_logger.debug("Got interrupted while waiting.");
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for adding port profile.");
|
||||
}
|
||||
@ -84,7 +105,7 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for updating port profile.");
|
||||
}
|
||||
@ -96,7 +117,7 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for deleting port profile.");
|
||||
}
|
||||
@ -109,7 +130,7 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for adding/updating policy map.");
|
||||
}
|
||||
@ -121,7 +142,7 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for deleting policy map.");
|
||||
}
|
||||
@ -140,7 +161,7 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for adding policy map.");
|
||||
}
|
||||
@ -153,7 +174,22 @@ public class NetconfHelper {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
parseReply(receive());
|
||||
parseOkReply(receive());
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for removing policy map.");
|
||||
}
|
||||
}
|
||||
|
||||
public void getPortProfileByName(String name) throws CloudRuntimeException {
|
||||
String command = VsmCommand.getPortProfile(name);
|
||||
if (command != null) {
|
||||
command = command.concat(SSH_NETCONF_TERMINATOR);
|
||||
send(command);
|
||||
// parse the rpc reply.
|
||||
VsmPortProfileResponse response = new VsmPortProfileResponse(receive().trim());
|
||||
if (!response.isResponseOk()) {
|
||||
throw new CloudRuntimeException("Error response while getting the port profile details.");
|
||||
}
|
||||
} else {
|
||||
throw new CloudRuntimeException("Error generating rpc request for removing policy map.");
|
||||
}
|
||||
@ -177,49 +213,122 @@ public class NetconfHelper {
|
||||
}
|
||||
|
||||
private String receive() {
|
||||
byte[] buffer = new byte[8192];
|
||||
String response = new String("");
|
||||
InputStream inputStream = _session.getStdout();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (inputStream.available() == 0) {
|
||||
int conditions = _session.waitForCondition(ChannelCondition.STDOUT_DATA
|
||||
| ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 3000);
|
||||
Delimiter delimiter = new Delimiter();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count = 0;
|
||||
|
||||
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
|
||||
break;
|
||||
// Read the input stream till we find the end sequence ']]>]]>'.
|
||||
while (true) {
|
||||
int data = inputStream.read();
|
||||
if (data != -1) {
|
||||
byte[] dataStream = delimiter.parse(data);
|
||||
if (delimiter.endReached()) {
|
||||
response += new String(buffer, 0, count);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dataStream != null) {
|
||||
for (int i = 0; i < dataStream.length; i++) {
|
||||
buffer[count] = dataStream[i];
|
||||
count++;
|
||||
if (count == 1024) {
|
||||
response += new String(buffer, 0, count);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new CloudRuntimeException("Error occured while reading from the stream: " + e.getMessage());
|
||||
}
|
||||
|
||||
return new String(buffer);
|
||||
return response;
|
||||
}
|
||||
|
||||
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);
|
||||
private void parseOkReply(String reply) throws CloudRuntimeException {
|
||||
VsmOkResponse response = new VsmOkResponse(reply.trim());
|
||||
if (!response.isResponseOk()) {
|
||||
throw new CloudRuntimeException(response.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class Delimiter {
|
||||
private boolean _endReached = false;
|
||||
|
||||
// Used to accumulate response read while searching for end of response.
|
||||
private byte[] _gatherResponse = new byte[6];
|
||||
|
||||
// Index into number of bytes read.
|
||||
private int _offset = 0;
|
||||
|
||||
// True if ']]>]]>' detected.
|
||||
boolean endReached() {
|
||||
return _endReached;
|
||||
}
|
||||
|
||||
// Parses the input stream and checks if end sequence is reached.
|
||||
byte[] parse(int input) throws RuntimeException {
|
||||
boolean collect = false;
|
||||
byte[] streamRead = null;
|
||||
|
||||
// Check if end sequence matched.
|
||||
switch (_offset) {
|
||||
case 0:
|
||||
if (input == ']') {
|
||||
collect = true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (input == ']') {
|
||||
collect = true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (input == '>') {
|
||||
collect = true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (input == ']') {
|
||||
collect = true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (input == ']') {
|
||||
collect = true;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (input == '>') {
|
||||
collect = true;
|
||||
_endReached = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Invalid index value: " + _offset);
|
||||
}
|
||||
|
||||
if (collect) {
|
||||
_gatherResponse[_offset++] = (byte)input;
|
||||
} else {
|
||||
// End sequence not yet reached. Return the stream of bytes collected so far.
|
||||
streamRead = new byte[_offset+1];
|
||||
for (int index = 0; index < _offset; ++index) {
|
||||
streamRead[index] = _gatherResponse[index];
|
||||
}
|
||||
|
||||
streamRead[_offset] = (byte) input;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
return streamRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +253,43 @@ public class VsmCommand {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPortProfile(String name) {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
DOMImplementation domImpl = docBuilder.getDOMImplementation();
|
||||
Document doc = createDocument(domImpl);
|
||||
|
||||
Element get = doc.createElement("nf:get");
|
||||
doc.getDocumentElement().appendChild(get);
|
||||
|
||||
Element filter = doc.createElement("nf:filter");
|
||||
filter.setAttribute("type", "subtree");
|
||||
get.appendChild(filter);
|
||||
|
||||
// Create the show port-profile name <profile-name> command.
|
||||
Element show = doc.createElement("show");
|
||||
filter.appendChild(show);
|
||||
Element portProfile = doc.createElement("port-profile");
|
||||
show.appendChild(portProfile);
|
||||
Element nameNode = doc.createElement("name");
|
||||
portProfile.appendChild(nameNode);
|
||||
|
||||
// Profile name
|
||||
Element profileName = doc.createElement("profile_name");
|
||||
profileName.setTextContent(name);
|
||||
nameNode.appendChild(profileName);
|
||||
|
||||
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();
|
||||
|
||||
23
utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmOkResponse.java
Normal file
23
utils/src/com/cloud/utils/cisco/n1kv/vsm/VsmOkResponse.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.cloud.utils.cisco.n1kv.vsm;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
public class VsmOkResponse extends VsmResponse {
|
||||
|
||||
VsmOkResponse(String response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
protected void parse(Element root) {
|
||||
NodeList list = root.getElementsByTagName("nf:rpc-error");
|
||||
if (list.getLength() == 0) {
|
||||
// No rpc-error tag; means response was ok.
|
||||
assert(root.getElementsByTagName("nf:ok").getLength() > 0);
|
||||
_responseOk = true;
|
||||
} else {
|
||||
parseError(list.item(0));
|
||||
_responseOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.cloud.utils.cisco.n1kv.vsm;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
public class VsmPortProfileResponse extends VsmResponse {
|
||||
VsmPortProfileResponse(String response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
protected void parse(Element root) {
|
||||
NodeList list = root.getElementsByTagName("nf:rpc-error");
|
||||
if (list.getLength() == 0) {
|
||||
// No rpc-error tag; means response was ok.
|
||||
assert(root.getElementsByTagName("nf:ok").getLength() > 0);
|
||||
_responseOk = true;
|
||||
} else {
|
||||
super.parseError(list.item(0));
|
||||
_responseOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,7 @@ import org.xml.sax.InputSource;
|
||||
import java.io.StringReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class VsmResponse {
|
||||
public abstract class VsmResponse {
|
||||
|
||||
// Following error tags, error types and severity have been taken from RFC 4741.
|
||||
public enum ErrorTag {
|
||||
@ -56,16 +56,16 @@ public class VsmResponse {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(VsmResponse.class);
|
||||
|
||||
private String _xmlResponse;
|
||||
private Document _docResponse;
|
||||
private boolean _responseOk;
|
||||
protected String _xmlResponse;
|
||||
protected Document _docResponse;
|
||||
protected boolean _responseOk;
|
||||
|
||||
private ErrorTag _tag;
|
||||
private ErrorType _type;
|
||||
private ErrorSeverity _severity;
|
||||
private String _path;
|
||||
private String _message;
|
||||
private String _info;
|
||||
protected ErrorTag _tag;
|
||||
protected ErrorType _type;
|
||||
protected ErrorSeverity _severity;
|
||||
protected String _path;
|
||||
protected String _message;
|
||||
protected String _info;
|
||||
|
||||
VsmResponse(String response) {
|
||||
_xmlResponse = response;
|
||||
@ -117,19 +117,9 @@ public class VsmResponse {
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
private void parse(Element root) {
|
||||
NodeList list = root.getElementsByTagName("nf:rpc-error");
|
||||
if (list.getLength() == 0) {
|
||||
// No rpc-error tag; means response was ok.
|
||||
assert(root.getElementsByTagName("nf:ok").getLength() > 0);
|
||||
_responseOk = true;
|
||||
} else {
|
||||
parseError(list.item(0));
|
||||
_responseOk = false;
|
||||
}
|
||||
}
|
||||
protected abstract void parse(Element root);
|
||||
|
||||
private void parseError(Node element) {
|
||||
protected void parseError(Node element) {
|
||||
Element rpcError = (Element) element;
|
||||
|
||||
try {
|
||||
@ -155,7 +145,7 @@ public class VsmResponse {
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorTag getErrorTag(String tagText) {
|
||||
protected ErrorTag getErrorTag(String tagText) {
|
||||
ErrorTag tag = ErrorTag.InUse;
|
||||
|
||||
if (tagText.equals("in-use")) {
|
||||
@ -202,7 +192,7 @@ public class VsmResponse {
|
||||
}
|
||||
|
||||
// Helper routine to check for the response received.
|
||||
private void printResponse() {
|
||||
protected void printResponse() {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user