From 15c63e8b46e7a3cf6f6ebd32d56a8fc75add97d0 Mon Sep 17 00:00:00 2001
From: Russell Haering <russell.haering@rackspace.com>
Date: Sat, 21 Dec 2013 17:22:09 -0800
Subject: [PATCH] begin hacking on command execution

---
 teeth_agent/base.py    |  7 ++++++-
 teeth_agent/errors.py  | 36 ++++++++++++++++++++++++++++++++++++
 teeth_agent/standby.py | 29 +++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 teeth_agent/errors.py

diff --git a/teeth_agent/base.py b/teeth_agent/base.py
index c9c82a564..2df99446b 100644
--- a/teeth_agent/base.py
+++ b/teeth_agent/base.py
@@ -22,6 +22,7 @@ from teeth_rest import encoding
 from werkzeug import serving
 
 from teeth_agent import api
+from teeth_agent import errors
 
 
 class TeethAgentStatus(encoding.Serializable):
@@ -46,6 +47,7 @@ class BaseTeethAgent(object):
         self.started_at = None
         self.mode = mode
         self.api = api.TeethAgentAPIServer(self)
+        self.command_map = {}
 
     def get_status(self):
         """Retrieve a serializable status."""
@@ -57,7 +59,10 @@ class BaseTeethAgent(object):
 
     def execute_command(self, command_name, **kwargs):
         """Execute an agent command."""
-        pass
+        if command_name not in self.command_map:
+            raise errors.InvalidCommandError(command_name)
+
+        self.comand_map[command_name](**kwargs)
 
     def run(self):
         """Run the Teeth Agent."""
diff --git a/teeth_agent/errors.py b/teeth_agent/errors.py
new file mode 100644
index 000000000..3dd4b8fe5
--- /dev/null
+++ b/teeth_agent/errors.py
@@ -0,0 +1,36 @@
+"""
+Copyright 2013 Rackspace, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+from teeth_rest import errors
+
+
+class InvalidCommandError(errors.InvalidContentError):
+    """Error which is raised when an unknown command is issued."""
+
+    messsage = 'Unknown command'
+
+    def __init__(self, command_name):
+        details = 'Command \'{}\' is unknown.'.format(command_name)
+        super(InvalidCommandError, self).__init__(details)
+
+
+class InvalidCommandParamsError(errors.InvalidContentError):
+    """Error which is raised when command parameters are invalid."""
+
+    message = 'Invalid command parameters'
+
+    def __init__(self, details):
+        super(InvalidCommandParamsError, self).__init__(details)
diff --git a/teeth_agent/standby.py b/teeth_agent/standby.py
index 70539fb02..504c6d7a4 100644
--- a/teeth_agent/standby.py
+++ b/teeth_agent/standby.py
@@ -15,8 +15,37 @@ limitations under the License.
 """
 
 from teeth_agent import base
+from teeth_agent import errors
 
 
 class StandbyAgent(base.BaseTeethAgent):
     def __init__(self, listen_host, listen_port):
         super(StandbyAgent, self).__init__(listen_host, listen_port, 'STANDBY')
+
+        self.command_map = {
+            'standby.cache_images': self.cache_images,
+        }
+
+    def _validate_image_info(self, image_info):
+        for field in ['id', 'urls', 'hashes']:
+            if field not in image_info:
+                msg = 'Image is missing \'{}\' field.'.format(field)
+                raise errors.InvalidCommandParamsError(msg)
+
+        if type(image_info['urls']) != list:
+            raise errors.InvalidCommandParamsError(
+                'Image \'urls\' must be a list.')
+
+        if type(image_info['hashes']) != dict:
+            raise errors.InvalidCommandParamsError(
+                'Image \'hashes\' must be a dictionary.')
+
+    def cache_images(self, image_infos):
+        if type(image_infos) != list:
+            raise errors.InvalidCommandParamsError(
+                '\'image_infos\' parameter must be a list.')
+
+        for image_info in image_infos:
+            self._validate_image_info(image_info)
+
+        # TODO(russellhaering): Actually cache images