commit 038383b1f633a0bf482cab64007207561ba7a7ac
Author: Dimitris Moraitis <dimo@unweb.me>
Date:   Sat Apr 2 19:45:19 2011 +0300

    detailed error handling & iso8601 compatibility - Refs #213 #363

diff --git a/README.develop b/README.develop
index c8795b7..57cf6ba 100644
--- a/README.develop
+++ b/README.develop
@@ -10,6 +10,7 @@ Synnefo depends on the following Python modules
 - simplejson
 - selenium
 - pyzmq-static
+- iso8601
 
 also, depending on the database engine of choice, on one of the following:
 - MySQL-python
diff --git a/api/handlers.py b/api/handlers.py
index a949e22..a6a130d 100644
--- a/api/handlers.py
+++ b/api/handlers.py
@@ -14,7 +14,7 @@ import random
 import string
 import logging
 from datetime import datetime, timedelta
-
+import iso8601
 from logic import backend, utils
 
 log = logging.getLogger('synnefo.api.handlers')
@@ -144,10 +144,9 @@ class ServerHandler(BaseHandler):
         try:
             changes_since = request.GET.get("changes-since", 0)
             if changes_since:
-                last_update = datetime.strptime(changes_since,
-                                                "%Y-%m-%dT%H:%M:%S")
+                last_update = iso8601.parse_date(changes_since)
                 #return a badRequest if the changes_since is older than a limit
-                if (datetime.now() - last_update >
+                if (datetime.now(last_update.tzinfo) - last_update >
                     timedelta(seconds=settings.POLL_LIMIT)):
                     raise fault.badRequest
                 virtual_servers = VirtualMachine.objects.filter(
diff --git a/ui/static/main.css b/ui/static/main.css
index 2c8ab93..edee20e 100644
--- a/ui/static/main.css
+++ b/ui/static/main.css
@@ -531,7 +531,7 @@ div.machine div.actions a.more {
     margin-top: 18px;
 }
 
-div.actions a:hover{
+div.actions a.enabled:hover{
     color: black !important;
     text-decoration: underline;
     display: block;
@@ -850,6 +850,12 @@ input.machine {
     color: #F49C1A;
 }
 
+#error-success div {
+    height: 142px;
+    overflow-y: auto;
+    overflow-x: hidden;
+}
+
 #error-success .close {
 	background-image: url(/static/close.png);
 	position: absolute;
@@ -1163,7 +1169,7 @@ div.actions a.selected, div.actions a.selected:hover {
     color: orange !important;
 }
 
-.message {
+.action_error .message, .action_error .code {
     display: none;
 }
 
diff --git a/ui/static/synnefo.js b/ui/static/synnefo.js
index 14b8672..1ae4bc9 100644
--- a/ui/static/synnefo.js
+++ b/ui/static/synnefo.js
@@ -11,8 +11,18 @@ function ISODateString(d){
 			pad(d.getUTCMonth()+1) + '-' +
 			pad(d.getUTCDate()) + 'T' +
 			pad(d.getUTCHours()) + ':' +
-			pad(d.getUTCMinutes())+ ':' +
-			pad(d.getUTCSeconds())
+			pad(d.getUTCMinutes()) + ':' +
+			pad(d.getUTCSeconds()) +'Z'
+}
+
+function parse_error(responseText){
+	var errors = [];
+	responseObj = JSON.parse(responseText);
+	//console.info(inp);
+	for (var err in responseObj){
+		errors[errors.length] = responseObj[err];
+	}
+	return errors;
 }
 
 // indexOf prototype for IE
@@ -221,7 +231,7 @@ function update_vms(interval) {
 			}
 			// as for now, just show an error message
 			try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
-			ajax_error(jqXHR.status);						
+			ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);						
 			return false;
 			},
         success: function(data, textStatus, jqXHR) {
@@ -239,11 +249,11 @@ function update_vms(interval) {
 			if (jqXHR.status == 200 || jqXHR.status == 203) {
 				try {
 					servers = data.servers;
-				} catch(err) { ajax_error('400');}
+				} catch(err) { ajax_error('400', undefined, 'Update VMs', jqXHR.responseText);}
 				update_machines_view(data);
 			} else if (jqXHR.status != 304){
 				try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
-				//ajax_error();						
+				//ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);					
 			}
 			return false;
         }
@@ -260,7 +270,7 @@ function update_images() {
         dataType: "json",
         timeout: TIMEOUT,
         error: function(jqXHR, textStatus, errorThrown) { 
-                    ajax_error(jqXHR.status);
+                    ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
                     },
         success: function(data, textStatus, jqXHR) {
             try {
@@ -365,7 +375,7 @@ function update_flavors() {
         timeout: TIMEOUT,
         error: function(jqXHR, textStatus, errorThrown) { 
             try {
-				ajax_error(jqXHR.status);
+				ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
 			} catch (err) {
 				ajax_error(err);
 			}
@@ -447,7 +457,7 @@ function create_vm(machineName, imageRef, flavorRef){
             "flavorRef" : flavorRef,
             "metadata" : {
                 "My Server Name" : machineName
-            },
+            }
         }
     };
 
@@ -459,13 +469,13 @@ function create_vm(machineName, imageRef, flavorRef){
     data: JSON.stringify(payload),
     timeout: TIMEOUT,
     error: function(jqXHR, textStatus, errorThrown) { 
-                ajax_error(jqXHR.status);
+                ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
            },
     success: function(data, textStatus, jqXHR) {
                 if ( jqXHR.status == '202') {
                     ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);                   
                 } else {
-                    ajax_error(jqXHR.status);
+                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
                 }
             }
     });
@@ -490,7 +500,7 @@ function reboot(serverIDs){
 		data: JSON.stringify(payload),
 		timeout: TIMEOUT,
 		error: function(jqXHR, textStatus, errorThrown) {
-                    display_failure(serverID, jqXHR.status, 'Reboot')
+                    display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText)
 				},
 		success: function(data, textStatus, jqXHR) {
 					if ( jqXHR.status == '202') {
@@ -502,7 +512,7 @@ function reboot(serverIDs){
 						// continue with the rest of the servers
 						reboot(serverIDs);
 					} else {
-						ajax_error(jqXHR.status, serverID);
+						ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
 					}
 				}
     });
@@ -529,7 +539,7 @@ function shutdown(serverIDs) {
         data: JSON.stringify(payload),
         timeout: TIMEOUT,
         error: function(jqXHR, textStatus, errorThrown) { 
-                    display_failure(serverID, jqXHR.status, 'Shutdown')
+                    display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText)
                     },
         success: function(data, textStatus, jqXHR) {
                     if ( jqXHR.status == '202') {
@@ -541,7 +551,7 @@ function shutdown(serverIDs) {
 						// continue with the rest of the servers			
                         shutdown(serverIDs);
                     } else {
-                        ajax_error(jqXHR.status, serverID);
+                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
                     }
                 }             
     });
@@ -566,7 +576,7 @@ function destroy(serverIDs) {
         data: JSON.stringify(payload),
         timeout: TIMEOUT,
         error: function(jqXHR, textStatus, errorThrown) { 
-                    display_failure(serverID, jqXHR.status, 'Destroy')
+                    display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText)
                     },
         success: function(data, textStatus, jqXHR) {
                     if ( jqXHR.status == '204') {
@@ -578,7 +588,7 @@ function destroy(serverIDs) {
 						// continue with the rest of the servers
                         destroy(serverIDs);
                     } else {
-                        ajax_error(jqXHR.status, serverID);
+                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
                     }
                 }             
     });
@@ -605,7 +615,7 @@ function start(serverIDs){
         data: JSON.stringify(payload),
         timeout: TIMEOUT,
         error: function(jqXHR, textStatus, errorThrown) { 
-                    display_failure(serverID, jqXHR.status, 'Start')
+                    display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText)
                     },
         success: function(data, textStatus, jqXHR) {
                     if ( jqXHR.status == '202') {
@@ -617,7 +627,7 @@ function start(serverIDs){
 						// continue with the rest of the servers						
                         start(serverIDs);
                     } else {
-                        ajax_error(jqXHR.status, serverID);
+                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
                     }
                 }
     });
diff --git a/ui/templates/home.html b/ui/templates/home.html
index a3e5e72..2f94376 100644
--- a/ui/templates/home.html
+++ b/ui/templates/home.html
@@ -64,15 +64,25 @@
         };
         
         // ajax error checking  
-        function ajax_error(status, serverID) {
+        function ajax_error(status, serverID, action, responseText) {
+            var serverName = '';
+            
+            if (serverID != undefined) {
+                serverName = $("#"+serverID).parent().parent().find("span.name").text();
+            }
+            
             // prepare the error message
             $("#error-success h3").text(ERRORS['HEADER']);
-            
-            if (ERRORS[status] != undefined) {
+            if (responseText != undefined){
+                var errors = parse_error(responseText);
+                if (serverName){
+                    serverName="<p><strong>Server:</strong> " + serverName + "</p>";
+                }
+                $("#error-success div").html("<p>"+(errors[0].message || ERRORS[errors[0].code]) +"</p>"+serverName +"<p><strong>Action:</strong> " + action + "</p><p><strong>Code</strong>: " + errors[0].code + "</p><p><strong>Details:</strong> " + errors[0].details +"</p>");
+            } else if (ERRORS[status] != undefined) {
                 if (serverID == undefined){
                     $("#error-success p").text(ERRORS[status]);
                 } else {
-                    var serverName = $("#"+serverID).parent().parent().find("a.name").text();
                     $("#error-success p").html("<b>" + serverName + "</b>" + ": " + ERRORS[status]);
                 }
             } else {
@@ -110,14 +120,14 @@
                     var CREATE_VM_SUCCESS_MSG = SUCCESS["CREATE_VM_SUCCESS_ONE"] + '<br />'
                         + SUCCESS["CREATE_VM_SUCCESS_TWO"] + '<br /><br />' + '<b>' + password + '</b>'
                         + '<br /><br />' + SUCCESS["CREATE_VM_SUCCESS_THREE"] ;
-                    $("#error-success p").html(CREATE_VM_SUCCESS_MSG);             
+                    $("#error-success div").html("<p>" + CREATE_VM_SUCCESS_MSG + "</p>");             
                 } else {
                     $("#error-success h3").text(SUCCESS['HEADER']);
-                    $("#error-success p").text(SUCCESS[status]);             
+                    $("#error-success div").text("<p>" + SUCCESS[status] + "</p>");             
                 }
             } else {
                 $("#error-success h3").text(SUCCESS['HEADER']);
-                $("#error-success p").text(SUCCESS['DEFAULT']);             
+                $("#error-success div").html("<p>" + SUCCESS['DEFAULT'] + "</p>");             
 
             }
 
diff --git a/ui/templates/list.html b/ui/templates/list.html
index 5cd330f..760fc7b 100644
--- a/ui/templates/list.html
+++ b/ui/templates/list.html
@@ -260,10 +260,10 @@ function display_success(serverID) {
 }
 
 // indicate that the requested action was not completed
-function display_failure(serverID, status, action) {
+function display_failure(status, serverID, action, responseText) {
 	osIcon = $('#'+serverID).parent().parent().find('.list-logo');
 	osIcon.attr('src',osIcon.attr('os'));
-    ajax_error(status, serverID);
+    ajax_error(status, serverID, action, responseText);
 }
 
 var vmTable = $("div.list table.list-machines").dataTable({
diff --git a/ui/templates/machines.html b/ui/templates/machines.html
index 8b21f89..6d45dfc 100644
--- a/ui/templates/machines.html
+++ b/ui/templates/machines.html
@@ -151,7 +151,7 @@
 
 <div class="modal" id="error-success">
     <h3>{% trans "Error!/Success!" %}</h3>
-    <p>{% trans "More details about the result"%}</p>
+    <div><p>{% trans "More details about the result"%}</p></div>
 </div>
 
 <!-- confirmation before executing an action -->
diff --git a/ui/templates/standard.html b/ui/templates/standard.html
index 795398e..1d98eb3 100644
--- a/ui/templates/standard.html
+++ b/ui/templates/standard.html
@@ -37,6 +37,7 @@
         </div>
         <div class="action_error" align="center">
             {% trans "<span class='orange'>Error</span> on" %} <span class="action">error action</span>
+            <span class="code"></span>            
             <span class="message"></span>
             <button class="details">Details</button>
         </div>
@@ -150,7 +151,7 @@ $("div.confirm_single .no").live('click', function(){
 
 $("div.action_error .details").live('click', function(){
     // remove the action from the pending list
-    ajax_error($(this).parent().children('.message').text());
+    ajax_error($(this).parent().children('.code').text(), undefined, $(this).parent().children('.action').text(), $(this).parent().children('.message').text());
     $(this).parent().hide();
 });
 
@@ -258,10 +259,11 @@ function display_success(serverID) {
 }
 
 // indicate that the requested action was not completed
-function display_failure(serverID, status, action) {
+function display_failure(status, serverID, action, responseText) {
     $('#'+serverID+ ' .spinner').hide();
     $('#'+serverID+ ' .action_error .action').text(action);
-    $('#'+serverID+ ' .action_error .message').text(status);
+    $('#'+serverID+ ' .action_error .code').text(status);
+    $('#'+serverID+ ' .action_error .message').text(responseText);
     $('#'+serverID+ ' .action_error').show();
     
 }
