From e73cda9748a8726531dba4b309783dde1597549a Mon Sep 17 00:00:00 2001
From: Quentin Smith <quentin@mit.edu>
Date: Sun, 9 Aug 2009 21:07:28 -0400
Subject: [PATCH] Make commands work from list and info pages

svn path=/package_branches/invirt-web/cherrypy-rebased/; revision=2684
---
 code/controls.py            |   23 ++++++++--------
 code/main.py                |   61 +++++++++++++++++++++----------------------
 code/templates/command.mako |    9 +++++++
 code/templates/command.tmpl |   16 ------------
 code/templates/info.mako    |    2 +-
 code/templates/list.mako    |    4 +--
 6 files changed, 54 insertions(+), 61 deletions(-)
 create mode 100644 code/templates/command.mako
 delete mode 100644 code/templates/command.tmpl

diff --git a/code/controls.py b/code/controls.py
index e32b1c2..c51a6ba 100644
--- a/code/controls.py
+++ b/code/controls.py
@@ -204,17 +204,18 @@ def deleteVM(machine):
         session.rollback()
         raise
 
-def commandResult(username, state, fields):
+def commandResult(username, state, command_name, machine_id, fields):
     start_time = 0
-    machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
-    action = fields.getfirst('action')
-    cdrom = fields.getfirst('cdrom')
+    machine = validation.Validate(username, state, machine_id=machine_id).machine
+    action = command_name
+    cdrom = fields.get('cdrom')
+    if not cdrom:
+        cdrom = None
     if cdrom is not None and not CDROM.query().filter_by(cdrom_id=cdrom).one():
         raise CodeError("Invalid cdrom type '%s'" % cdrom)    
-    if action not in ('Reboot', 'Power on', 'Power off', 'Shutdown', 
-                      'Delete VM'):
+    if action not in "reboot create destroy shutdown delete".split(" "):
         raise CodeError("Invalid action '%s'" % action)
-    if action == 'Reboot':
+    if action == 'reboot':
         if cdrom is not None:
             out, err = remctl('control', machine.name, 'reboot', cdrom,
                               err=True)
@@ -230,13 +231,13 @@ def commandResult(username, state, fields):
                 print >> sys.stderr, err
                 raise CodeError('ERROR on remctl')
                 
-    elif action == 'Power on':
+    elif action == 'create':
         if validation.maxMemory(username, state, machine) < machine.memory:
             raise InvalidInput('action', 'Power on',
                                "You don't have enough free RAM quota "
                                "to turn on this machine.")
         bootMachine(machine, cdrom)
-    elif action == 'Power off':
+    elif action == 'destroy':
         out, err = remctl('control', machine.name, 'destroy', err=True)
         if err:
             if re.match("machine '.*' is not on", err):
@@ -246,7 +247,7 @@ def commandResult(username, state, fields):
                 print >> sys.stderr, 'Error on power off:'
                 print >> sys.stderr, err
                 raise CodeError('ERROR on remctl')
-    elif action == 'Shutdown':
+    elif action == 'shutdown':
         out, err = remctl('control', machine.name, 'shutdown', err=True)
         if err:
             if re.match("machine '.*' is not on", err):
@@ -256,7 +257,7 @@ def commandResult(username, state, fields):
                 print >> sys.stderr, 'Error on Shutdown:'
                 print >> sys.stderr, err
                 raise CodeError('ERROR on remctl')
-    elif action == 'Delete VM':
+    elif action == 'delete':
         deleteVM(machine)
 
     d = dict(user=username,
diff --git a/code/main.py b/code/main.py
index d938450..ba892bf 100755
--- a/code/main.py
+++ b/code/main.py
@@ -58,10 +58,12 @@ class InvirtWeb(View):
 
     @cherrypy.expose
     @cherrypy.tools.mako(filename="/list.mako")
-    def list(self):
+    def list(self, result=None):
         """Handler for list requests."""
         checkpoint.checkpoint('Getting list dict')
         d = getListDict(cherrypy.request.login, cherrypy.request.state)
+        if result is not None:
+            d['result'] = result
         checkpoint.checkpoint('Got list dict')
         return d
     index=list
@@ -214,6 +216,33 @@ console will suffer artifacts.
                      port=port,
                      authtoken=token)
             return d
+        @cherrypy.expose
+        @cherrypy.tools.mako(filename="/command.mako")
+        def command(self, command_name, machine_id, **kwargs):
+            """Handler for running commands like boot and delete on a VM."""
+            if cherrypy.request.method != "POST":
+                raise InvalidInput("request.method", command_name, "You must execute commands via POST")
+            back = kwargs.get('back', None)
+            try:
+                d = controls.commandResult(cherrypy.request.login, cherrypy.request.state, command_name, machine_id, kwargs)
+                if d['command'] == 'Delete VM':
+                    back = 'list'
+            except InvalidInput, err:
+                if not back:
+                    raise
+                print >> sys.stderr, err
+                result = err
+            else:
+                result = 'Success!'
+                if not back:
+                    return d
+            if back == 'list':
+                cherrypy.request.state.clear() #Changed global state
+                raise cherrypy.InternalRedirect('/list?result=%s' % urllib.quote(result))
+            elif back == 'info':
+                raise cherrypy.HTTPRedirect(cherrypy.request.base + '/machine/%d/' % machine_id, status=303)
+            else:
+                raise InvalidInput('back', back, 'Not a known back page.')
 
     machine = MachineView()
 
@@ -431,35 +460,6 @@ def getDiskInfo(data_dict, machine):
         data_dict['%s_size' % name] = "%0.1f GiB" % (disk.size / 1024.)
     return disk_fields
 
-def command(username, state, path, fields):
-    """Handler for running commands like boot and delete on a VM."""
-    back = fields.getfirst('back')
-    try:
-        d = controls.commandResult(username, state, fields)
-        if d['command'] == 'Delete VM':
-            back = 'list'
-    except InvalidInput, err:
-        if not back:
-            raise
-        print >> sys.stderr, err
-        result = err
-    else:
-        result = 'Success!'
-        if not back:
-            return templates.command(searchList=[d])
-    if back == 'list':
-        state.clear() #Changed global state
-        d = getListDict(username, state)
-        d['result'] = result
-        return templates.list(searchList=[d])
-    elif back == 'info':
-        machine = validation.Validate(username, state, machine_id=fields.getfirst('machine_id')).machine
-        return ({'Status': '303 See Other',
-                 'Location': 'info?machine_id=%d' % machine.machine_id},
-                "You shouldn't see this message.")
-    else:
-        raise InvalidInput('back', back, 'Not a known back page.')
-
 def modifyDict(username, state, fields):
     """Modify a machine as specified by CGI arguments.
 
@@ -651,7 +651,6 @@ def throwError(_, __, ___, ____):
     raise RuntimeError("test of the emergency broadcast system")
 
 mapping = dict(
-               command=command,
                modify=modify,
                create=create,
                unauth=unauthFront,
diff --git a/code/templates/command.mako b/code/templates/command.mako
new file mode 100644
index 0000000..17a16ed
--- /dev/null
+++ b/code/templates/command.mako
@@ -0,0 +1,9 @@
+<%page expression_filter="h" />
+<%inherit file="skeleton.mako" />
+
+<%def name="title()">
+$command ${machine.name}
+</%def>
+
+<p>${command} ${machine.name} was successful.</p>
+<p><a href="list">Return</a></p>
diff --git a/code/templates/command.tmpl b/code/templates/command.tmpl
deleted file mode 100644
index 904cba7..0000000
--- a/code/templates/command.tmpl
+++ /dev/null
@@ -1,16 +0,0 @@
-#from skeleton import skeleton
-#extends skeleton
-
-#def title
-$command $machine.name
-#end def
-
-
-#def body
-<p>$command ${machine.name} was successful.</p>
-#if $command == "Delete VM" or True
-<p><a href="list">Return</a></p>
-#else
-<p><a href="info?machine_id=${machine.machine_id}">Return</a></p>
-#end if
-#end def
diff --git a/code/templates/info.mako b/code/templates/info.mako
index b98e7c2..d6a1cfb 100644
--- a/code/templates/info.mako
+++ b/code/templates/info.mako
@@ -28,7 +28,7 @@ Info on ${machine.name}
 % endif
 <%def name="command_button(title, value, cdrom=False, extra='')">
 <form action="machine/${machine.machine_id}/command/${value}" method="POST">
-  <input type="hidden" name="back" value="machine/${machine.machine_id}/info" />
+  <input type="hidden" name="back" value="info" />
   <input type="submit" class="button" name="action" value="${title}" ${extra | n}/>
 % if cdrom:
   Boot CD: ${self.fn.cdromList()}
diff --git a/code/templates/list.mako b/code/templates/list.mako
index 71afd55..176fe14 100644
--- a/code/templates/list.mako
+++ b/code/templates/list.mako
@@ -84,11 +84,11 @@ ${self.fn.cdromList(defaults.cdrom, "$('cd_or_auto_cd').checked = true;$('autoin
 <%def name="machineRow(machine)">
       <tr> 
 	<td rowspan="2">
-	  <form action="command" method="post">
+	  <form action="machine/${machine.machine_id}/command/${'shutdown' if machine.uptime else 'create'}" method="post">
 	    <input type="hidden" name="back" value="list"/>
 	    <input type="hidden" name="machine_id"
 		   value="${machine.machine_id}"/>
-<input type="submit" class="power ${'on' if machine.uptime else 'off'}" name="action" value="${'Power off' if machine.uptime else 'Power on'}"\
+<input type="submit" class="power ${'on' if machine.uptime else 'off'}" name="action" value="${'Shutdown' if machine.uptime else 'Power on'}"\
 % if machine.uptime:
  onclick="return confirm('Are you sure you want to power off this VM?');"
 % endif
-- 
1.7.9.5