From 6e127030ef9fbd6565777403eee73f015bd2509c Mon Sep 17 00:00:00 2001
From: Josh Gachnang <josh.gachnang@rackspace.com>
Date: Tue, 11 Mar 2014 13:31:19 -0700
Subject: [PATCH] Fixing ordereddict and format string for py26

---
 requirements.txt                  |  3 ++-
 teeth_agent/agent.py              | 10 +++++-----
 teeth_agent/api.py                |  2 +-
 teeth_agent/base.py               |  8 ++++----
 teeth_agent/configdrive.py        |  8 ++++----
 teeth_agent/errors.py             | 13 +++++++------
 teeth_agent/hardware.py           | 11 ++++++-----
 teeth_agent/log.py                |  2 +-
 teeth_agent/overlord_agent_api.py |  4 ++--
 teeth_agent/standby.py            |  4 ++--
 teeth_agent/tests/configdrive.py  |  6 +++---
 teeth_agent/utils.py              | 25 +++++++++++++++++++++++++
 12 files changed, 62 insertions(+), 34 deletions(-)
 create mode 100644 teeth_agent/utils.py

diff --git a/requirements.txt b/requirements.txt
index fa35225cc..65ae9972f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,4 +3,5 @@ requests==2.0.0
 cherrypy==3.2.4
 stevedore==0.14
 -e git+https://github.com/racker/teeth-rest.git@e876c0fddd5ce2f5223ab16936f711b0d57e19c4#egg=teeth_rest
-structlog
\ No newline at end of file
+structlog
+ordereddict
\ No newline at end of file
diff --git a/teeth_agent/agent.py b/teeth_agent/agent.py
index 3632bd0cb..b14863941 100644
--- a/teeth_agent/agent.py
+++ b/teeth_agent/agent.py
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 """
 
-import collections
 import random
 import threading
 import time
@@ -31,6 +30,7 @@ from teeth_agent import base
 from teeth_agent import errors
 from teeth_agent import hardware
 from teeth_agent import overlord_agent_api
+from teeth_agent import utils
 
 
 class TeethAgentStatus(encoding.Serializable):
@@ -41,7 +41,7 @@ class TeethAgentStatus(encoding.Serializable):
 
     def serialize(self, view):
         """Turn the status into a dict."""
-        return collections.OrderedDict([
+        return utils.get_ordereddict([
             ('mode', self.mode),
             ('started_at', self.started_at),
             ('version', self.version),
@@ -113,7 +113,7 @@ class TeethAgent(object):
         self.mode_implementation = None
         self.version = pkg_resources.get_distribution('teeth-agent').version
         self.api = api.TeethAgentAPIServer(self)
-        self.command_results = collections.OrderedDict()
+        self.command_results = utils.get_ordereddict()
         self.heartbeater = TeethAgentHeartbeater(self)
         self.hardware = hardware.get_manager()
         self.command_lock = threading.Lock()
@@ -161,10 +161,10 @@ class TeethAgent(object):
                 self.mode_implementation = _load_mode_implementation(mode_name)
             except Exception:
                 raise errors.InvalidCommandError(
-                    'Unknown mode: {}'.format(mode_name))
+                    'Unknown mode: {0}'.format(mode_name))
         elif self.get_mode_name().lower() != mode_name:
             raise errors.InvalidCommandError(
-                'Agent is already in {} mode'.format(self.get_mode_name()))
+                'Agent is already in {0} mode'.format(self.get_mode_name()))
 
     def execute_command(self, command_name, **kwargs):
         """Execute an agent command."""
diff --git a/teeth_agent/api.py b/teeth_agent/api.py
index 49a74aaef..32be905c5 100644
--- a/teeth_agent/api.py
+++ b/teeth_agent/api.py
@@ -28,7 +28,7 @@ class AgentCommand(object):
     def deserialize(cls, obj):
         for field in ['name', 'params']:
             if field not in obj:
-                msg = 'Missing command \'{}\' field.'.format(field)
+                msg = 'Missing command \'{0}\' field.'.format(field)
                 raise errors.InvalidContentError(msg)
 
         if type(obj['params']) != dict:
diff --git a/teeth_agent/base.py b/teeth_agent/base.py
index 3170678a6..03b611e03 100644
--- a/teeth_agent/base.py
+++ b/teeth_agent/base.py
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 """
 
-import collections
 import threading
 import uuid
 
@@ -23,6 +22,7 @@ from teeth_rest import encoding
 from teeth_rest import errors as rest_errors
 
 from teeth_agent import errors
+from teeth_agent import utils
 
 
 class AgentCommandStatus(object):
@@ -41,7 +41,7 @@ class BaseCommandResult(encoding.Serializable):
         self.command_result = None
 
     def serialize(self, view):
-        return collections.OrderedDict([
+        return utils.get_ordereddict([
             ('id', self.id),
             ('command_name', self.command_name),
             ('command_params', self.command_params),
@@ -79,7 +79,7 @@ class AsyncCommandResult(BaseCommandResult):
         self.execute_method = execute_method
         self.command_state_lock = threading.Lock()
 
-        thread_name = 'agent-command-{}'.format(self.id)
+        thread_name = 'agent-command-{0}'.format(self.id)
         self.execution_thread = threading.Thread(target=self.run,
                                                  name=thread_name)
 
@@ -126,7 +126,7 @@ class BaseAgentMode(object):
     def execute(self, command_name, **kwargs):
         if command_name not in self.command_map:
             raise errors.InvalidCommandError(
-                'Unknown command: {}'.format(command_name))
+                'Unknown command: {0}'.format(command_name))
 
         result = self.command_map[command_name](command_name, **kwargs)
 
diff --git a/teeth_agent/configdrive.py b/teeth_agent/configdrive.py
index a65522e45..d4bec9b0e 100644
--- a/teeth_agent/configdrive.py
+++ b/teeth_agent/configdrive.py
@@ -15,15 +15,15 @@ limitations under the License.
 """
 
 import base64
-import collections
 import json
 import os
+from teeth_agent import utils
 
 
 class ConfigDriveWriter(object):
     def __init__(self):
         self.metadata = {}
-        self.files = collections.OrderedDict()
+        self.files = utils.get_ordereddict()
 
     def add_metadata(self, key, value):
         self.metadata[key] = value
@@ -43,7 +43,7 @@ class ConfigDriveWriter(object):
             metadata['files'] = []
         filenumber = 0
         for filepath, contents in self.files.iteritems():
-            content_path = '/content/{:04}'.format(filenumber)
+            content_path = '/content/{0:04}'.format(filenumber)
             file_info = {
                 'content_path': content_path,
                 'path': filepath
@@ -57,7 +57,7 @@ class ConfigDriveWriter(object):
             filenumber += 1
 
         json_metadata = json.dumps(metadata)
-        metadata_path = '{}/{}/meta_data.json'.format(prefix, version)
+        metadata_path = '{0}/{1}/meta_data.json'.format(prefix, version)
         metadata_path = os.path.join(location, metadata_path)
         with open(metadata_path, 'wb') as f:
             f.write(json_metadata)
diff --git a/teeth_agent/errors.py b/teeth_agent/errors.py
index 0e1abf1dd..3d0f870b5 100644
--- a/teeth_agent/errors.py
+++ b/teeth_agent/errors.py
@@ -47,7 +47,7 @@ class InvalidCommandParamsError(errors.InvalidContentError):
 
 class RequestedObjectNotFoundError(errors.NotFound):
     def __init__(self, type_descr, obj_id):
-        details = '{} with id {} not found.'.format(type_descr, obj_id)
+        details = '{0} with id {1} not found.'.format(type_descr, obj_id)
         super(RequestedObjectNotFoundError, self).__init__(details)
         self.details = details
 
@@ -78,7 +78,7 @@ class ImageDownloadError(errors.RESTError):
 
     def __init__(self, image_id):
         super(ImageDownloadError, self).__init__()
-        self.details = 'Could not download image with id {}.'.format(image_id)
+        self.details = 'Could not download image with id {0}.'.format(image_id)
 
 
 class ImageChecksumError(errors.RESTError):
@@ -88,7 +88,7 @@ class ImageChecksumError(errors.RESTError):
 
     def __init__(self, image_id):
         super(ImageChecksumError, self).__init__()
-        self.details = 'Image with id {} failed to verify against checksum.'
+        self.details = 'Image with id {0} failed to verify against checksum.'
         self.details = self.details.format(image_id)
 
 
@@ -99,7 +99,7 @@ class ImageWriteError(errors.RESTError):
 
     def __init__(self, exit_code, device):
         super(ImageWriteError, self).__init__()
-        self.details = 'Writing image to device {} failed with exit code {}.'
+        self.details = 'Writing image to device {0} failed with exit code {1}.'
         self.details = self.details.format(device, exit_code)
 
 
@@ -111,7 +111,8 @@ class ConfigDriveWriteError(errors.RESTError):
     message = 'Error writing configdrive to device.'
 
     def __init__(self, exit_code, device):
-        details = 'Writing configdrive to device {} failed with exit code {}.'
+        details = 'Writing configdrive to device {0} failed with exit code ' \
+                  '{1}.'
         details = details.format(device, exit_code)
         super(ConfigDriveWriteError, self).__init__(details)
         self.details = details
@@ -124,5 +125,5 @@ class SystemRebootError(errors.RESTError):
 
     def __init__(self, exit_code):
         super(SystemRebootError, self).__init__()
-        self.details = 'Reboot script failed with exit code {}.'
+        self.details = 'Reboot script failed with exit code {0}.'
         self.details = self.details.format(exit_code)
diff --git a/teeth_agent/hardware.py b/teeth_agent/hardware.py
index bc79db391..8753fd121 100644
--- a/teeth_agent/hardware.py
+++ b/teeth_agent/hardware.py
@@ -15,13 +15,14 @@ limitations under the License.
 """
 
 import abc
-import collections
 import os
 import subprocess
 
 import stevedore
 import structlog
 
+from teeth_agent import utils
+
 from teeth_rest import encoding
 
 _global_manager = None
@@ -50,7 +51,7 @@ class HardwareInfo(encoding.Serializable):
         self.id = id
 
     def serialize(self, view):
-        return collections.OrderedDict([
+        return utils.get_ordereddict([
             ('type', self.type),
             ('id', self.id),
         ])
@@ -104,19 +105,19 @@ class GenericHardwareManager(HardwareManager):
         return HardwareSupport.GENERIC
 
     def _get_interface_info(self, interface_name):
-        addr_path = '{}/class/net/{}/address'.format(self.sys_path,
+        addr_path = '{0}/class/net/{1}/address'.format(self.sys_path,
                                                      interface_name)
         addr_file = open(addr_path, 'r')
         mac_addr = addr_file.read().strip()
         return NetworkInterface(interface_name, mac_addr)
 
     def _is_device(self, interface_name):
-        device_path = '{}/class/net/{}/device'.format(self.sys_path,
+        device_path = '{0}/class/net/{1}/device'.format(self.sys_path,
                                                       interface_name)
         return os.path.exists(device_path)
 
     def list_network_interfaces(self):
-        iface_names = os.listdir('{}/class/net'.format(self.sys_path))
+        iface_names = os.listdir('{0}/class/net'.format(self.sys_path))
         return [self._get_interface_info(name)
                 for name in iface_names
                 if self._is_device(name)]
diff --git a/teeth_agent/log.py b/teeth_agent/log.py
index 1429f9932..a67ea3a64 100644
--- a/teeth_agent/log.py
+++ b/teeth_agent/log.py
@@ -49,7 +49,7 @@ def _format_event(logger, method, event):
         # index 1 is the key name
         keys = [item[1] for item in keys]
         missing_keys = list(set(keys) - set(event))
-        raise KeyError("Log formatter missing keys: {}, cannot format."
+        raise KeyError("Log formatter missing keys: {0}, cannot format."
                        .format(missing_keys))
     event['event'] = formatted
     return event
diff --git a/teeth_agent/overlord_agent_api.py b/teeth_agent/overlord_agent_api.py
index 7fa41ade5..1ef250a32 100644
--- a/teeth_agent/overlord_agent_api.py
+++ b/teeth_agent/overlord_agent_api.py
@@ -62,7 +62,7 @@ class APIClient(object):
             raise errors.HeartbeatError(str(e))
 
         if response.status_code != requests.codes.NO_CONTENT:
-            msg = 'Invalid status code: {}'.format(response.status_code)
+            msg = 'Invalid status code: {0}'.format(response.status_code)
             raise errors.HeartbeatError(msg)
 
         try:
@@ -80,7 +80,7 @@ class APIClient(object):
         response = self._request('GET', path)
 
         if response.status_code != requests.codes.OK:
-            msg = 'Invalid status code: {}'.format(response.status_code)
+            msg = 'Invalid status code: {0}'.format(response.status_code)
             raise errors.OverlordAPIError(msg)
 
         try:
diff --git a/teeth_agent/standby.py b/teeth_agent/standby.py
index 43df5c621..efdc7df92 100644
--- a/teeth_agent/standby.py
+++ b/teeth_agent/standby.py
@@ -35,7 +35,7 @@ def _configdrive_location():
 
 
 def _image_location(image_info):
-    return '/tmp/{}'.format(image_info['id'])
+    return '/tmp/{0}'.format(image_info['id'])
 
 
 def _path_to_script(script):
@@ -139,7 +139,7 @@ def _validate_image_info(image_info=None, **kwargs):
 
     for field in ['id', 'urls', 'hashes']:
         if field not in image_info:
-            msg = 'Image is missing \'{}\' field.'.format(field)
+            msg = 'Image is missing \'{0}\' field.'.format(field)
             raise errors.InvalidCommandParamsError(msg)
 
     if type(image_info['urls']) != list or not image_info['urls']:
diff --git a/teeth_agent/tests/configdrive.py b/teeth_agent/tests/configdrive.py
index ea4f80f73..915d939a7 100644
--- a/teeth_agent/tests/configdrive.py
+++ b/teeth_agent/tests/configdrive.py
@@ -17,12 +17,12 @@ limitations under the License.
 from __future__ import unicode_literals
 
 import base64
-import collections
 import json
 import mock
 import unittest
 
 from teeth_agent import configdrive
+from teeth_agent import utils
 
 
 class ConfigDriveWriterTestCase(unittest.TestCase):
@@ -68,7 +68,7 @@ class ConfigDriveWriterTestCase(unittest.TestCase):
         metadata = {'admin_pass': 'password', 'hostname': 'test'}
         for k, v in metadata.iteritems():
             self.writer.add_metadata(k, v)
-        files = collections.OrderedDict([
+        files = utils.get_ordereddict([
             ('/etc/conf0', 'contents0'),
             ('/etc/conf1', 'contents1'),
         ])
@@ -118,7 +118,7 @@ class ConfigDriveWriterTestCase(unittest.TestCase):
     @mock.patch('__builtin__.open', autospec=True)
     def test_write_configdrive(self, open_mock, makedirs_mock):
         metadata = {'admin_pass': 'password', 'hostname': 'test'}
-        files = collections.OrderedDict([
+        files = utils.get_ordereddict([
             ('/etc/conf0', base64.b64encode('contents0')),
             ('/etc/conf1', base64.b64encode('contents1')),
         ])
diff --git a/teeth_agent/utils.py b/teeth_agent/utils.py
new file mode 100644
index 000000000..bef52d3c5
--- /dev/null
+++ b/teeth_agent/utils.py
@@ -0,0 +1,25 @@
+"""
+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.
+"""
+import collections
+import ordereddict
+
+
+def get_ordereddict(*args, **kwargs):
+    """A fix for py26 not having ordereddict."""
+    try:
+        return collections.OrderedDict(*args, **kwargs)
+    except AttributeError:
+        return ordereddict.OrderedDict(*args, **kwargs)