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)