Root Exception classes in Cloudstack">Root Exception classes in Cloudstack

There are two "root" exception classes in Cloudstack which serve as the base classes for most of the Cloudstack exception classes. They are listed below -

CloudException

RuntimeCloudException

Exception flow control">Exception flow control

When a command is issued to the Cloudstack Management Server, whether it synchronous or asynchronous, it could fail due to various reasons. Cloudstack conveys failure to the requesting client via HTTP Exception responses. An body of the HTTP response that carries a failure message (an exception) contains either JSON or XML output that can be parsed for further interpretation of the actual error that was encountered.

Given below is a list of generic points in the code flow where where exceptions can be thrown in the cloudstack code. The function names referred to here are ones that you will find in the cloudstack API code. Start off with the file server/src/com/cloud/api/ApiServer.java, and follow the code from the handle() method.

handle() {
// Gets an HTTP request made by a client. Preprocesses the request by adding pertinent fields.
try {
handlerequest() {
try {
response = queuecommand(<command>) {
dispatch(command object)

Unknown macro: { try
Unknown macro: { command.execute(); }
catch (Exception e)
Unknown macro: { process_exception(); rethrow exception; }
}

// Set the uuid translation flag to true. This will ensure that any database ids present in the exception object
// will be translated via a db lookup, into uuids.
return a serializedstring() of the command object's response object returned by dispatch.
}
} catch (Exception e)

Unknown macro: { // Handle different instances of cloudstack exceptions if (e instanceof InvalidParameterValueException)
Unknown macro: { // Carry out processing for this specific exception. }

else if (e instanceof PermissionDeniedException)

else ServerApiException
Unknown macro: { // Carry out processing for this specific exception. }
else
Unknown macro: { // default case }
}

return response to handle();
} //end of handlerequest()
} catch (ServerApiException e)

Unknown macro: { // Prepare an HTTP Exception Response. writeresponse(); }

catch (RuntimeException e)

} //end of handle()


Any exception can be thrown by a specific API's execute() method. All Cloustack commands' source is present under the api/src/com/cloud/api/commands/ directory. Within each *.java file in this directory, you will see an execute() function. You must dive into the specific implementation of whatever function execute() calls into to see what specific exception is thrown.

How to throw a cloudstack exception">How to throw a cloudstack exception

Exception responses in Cloudstack typically include a message, and mandatorily a Cloudstack error code. They may also optionally contain a list of <uuid, propertyname> key value pairs that would help in clearer error signaling to the client invoking the cloudstack API.

When any cloudstack exception is raised, a cloudstack error field will automatically be populated with a numerical value for that exception. This happens in the constructors of the two base exception classes we listed above. The list of error codes for each type of cloudstack exception is maintained in the file utils/src/com/cloud/utils/exception/CSExceptionErrorCode.java, whose class provides a routine to lookup the particular cloudstack exception's Cloudstack error code. This error code is different from the HTTP error code.

Before throwing an exception, we can optionally issue calls to one of the two flavors of the addProxyObject() function, if we wish to pack in any uuids of specific components - for example, a zone Id, a pod Id, Account Id, and so on. The addProxyObject() function can be called multiple times to populate the exception with as many Ids as we wish to pack into the exception. When invoking this function, we always pass the database id value of the property we wish to populate the exception with, and not the uuid. Either the name of the table that contains this database id's uuid or the Value Object (VO object) is passed to addProxyObject(). The VO object is the object that is mapped by cglib to the underlying MySQL database table.

Any response, whether an exception response or a success response, is always serialized by the API layer. This is done as part of the writeresponse() function in the code flow given above. This function uses two custom serializer routines - one that serializes the output into an XML format, and the other that does so into JSON (Java Script Object Notation) format.

Within these XML and JSON serializer routines, cloudstack code specifically looks for a list of <uuid, propertyname, table> tuples and does a look up of the uuid from the db id provided, and places this uuid and the property name in the output.

Hence, a sample output of a client API request that attempts to rename an existing domain to a name that already belongs to another existing domain would encounter an exception response that could look like -

In JSON -">In JSON -

HTTP/1.0 431 Failed to update specified domain id with name 'testdomain1' since it already exists in the system
Date: Fri, 16 Mar 2012 01:56:09 GMT
Server: HttpComponents/1.1
Content-Type: text/javascript
Connection: Close

{ "updatedomainresponse" : {"uuidList":[

Unknown macro: {"uuid"}

],"errorcode":431,"cserrorcode":4490,"errortext":"Failed to update specified domain id with name 'testdomain1' since it already exists in the system"} }

And in XML -">And in XML -

HTTP/1.0 431 Failed to update specified domain id with name 'testdomain1' since it already exists in the system
Date: Fri, 16 Mar 2012 01:56:09 GMT
Server: HttpComponents/1.1
Content-Type: text/xml
Connection: Close

<?xml version="1.0" encoding="ISO-8859-1"?><updatedomainresponse cloud-stack-version="3.0.0.2012-03-15T23:28:49Z"><uuidList><uuid>3d9192cf-c480-4fdc-9594-0c6f54131a14</uuid><uuidProperty>domainId</uuidProperty></uuidList><errorcode>431</errorcode><cserrorcode>4490</cserrorcode><errortext>Failed to update specified domain id with name 'testdomain1' since it already exists in the system</errortext><jobid>com.cloud.utils.IdentityProxy@3b94d235</jobid></updatedomainresponse>

AnnotationHelper.java">AnnotationHelper.java

The routine to help lookup the tablename given a reference to the VO object that is obtained from cglib (which creates dynamic proxy objects to map the java objects to database tables) is AnnotationHelper.getTableName(). This routine looks up a VO class’s Table annotation to get to the tablename, and if the VO class extends another VO class and so on, the routine will return the base class’s Table annotation. If you plan to add annotation read routines, please add them to the file AnnotationHelper.java.

Point to note">Point to note

Whenever throwing an exception, ensure that you call the addProxyObject() function if applicable.

  • No labels