From 5ebd2e97977cc7b2ef2625b72db6bad5de757998 Mon Sep 17 00:00:00 2001 From: Russell Haering Date: Fri, 11 Apr 2014 16:46:36 -0700 Subject: [PATCH] Organize agent extensions Move extensions under an ironic_python_agent.extensions module. This change also moves the @async_command() decorator into the base extension module. Change-Id: I4021fcc33a30f3460a31bca44a4bf776cd53d488 --- ironic_python_agent/agent.py | 2 +- ironic_python_agent/decorators.py | 41 ------------------- ironic_python_agent/extensions/__init__.py | 0 ironic_python_agent/{ => extensions}/base.py | 25 +++++++++++ ironic_python_agent/{ => extensions}/decom.py | 2 +- ironic_python_agent/{ => extensions}/flow.py | 5 +-- .../{ => extensions}/standby.py | 9 ++-- ironic_python_agent/tests/agent.py | 2 +- ironic_python_agent/tests/api.py | 2 +- .../tests/extensions/__init__.py | 0 .../tests/{ => extensions}/base.py | 2 +- .../tests/{ => extensions}/decom.py | 2 +- .../tests/{ => extensions}/flow.py | 7 ++-- .../tests/{ => extensions}/standby.py | 25 +++++++---- setup.cfg | 6 +-- 15 files changed, 59 insertions(+), 71 deletions(-) delete mode 100644 ironic_python_agent/decorators.py create mode 100644 ironic_python_agent/extensions/__init__.py rename ironic_python_agent/{ => extensions}/base.py (87%) rename ironic_python_agent/{ => extensions}/decom.py (93%) rename ironic_python_agent/{ => extensions}/flow.py (94%) rename ironic_python_agent/{ => extensions}/standby.py (96%) create mode 100644 ironic_python_agent/tests/extensions/__init__.py rename ironic_python_agent/tests/{ => extensions}/base.py (98%) rename ironic_python_agent/tests/{ => extensions}/decom.py (94%) rename ironic_python_agent/tests/{ => extensions}/flow.py (96%) rename ironic_python_agent/tests/{ => extensions}/standby.py (93%) diff --git a/ironic_python_agent/agent.py b/ironic_python_agent/agent.py index a611c04e1..39c8368c0 100644 --- a/ironic_python_agent/agent.py +++ b/ironic_python_agent/agent.py @@ -21,9 +21,9 @@ from stevedore import extension from wsgiref import simple_server from ironic_python_agent.api import app -from ironic_python_agent import base from ironic_python_agent import encoding from ironic_python_agent import errors +from ironic_python_agent.extensions import base from ironic_python_agent import hardware from ironic_python_agent import ironic_api_client from ironic_python_agent.openstack.common import log diff --git a/ironic_python_agent/decorators.py b/ironic_python_agent/decorators.py deleted file mode 100644 index 30f7cf7c6..000000000 --- a/ironic_python_agent/decorators.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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 functools - -from ironic_python_agent import base - - -def async_command(validator=None): - """Will run the command in an AsyncCommandResult in its own thread. - command_name is set based on the func name and command_params will - be whatever args/kwargs you pass into the decorated command. - """ - def async_decorator(func): - @functools.wraps(func) - def wrapper(self, command_name, **command_params): - # Run a validator before passing everything off to async. - # validators should raise exceptions or return silently. - if validator: - validator(self, **command_params) - - # bind self to func so that AsyncCommandResult doesn't need to - # know about the mode - bound_func = functools.partial(func, self) - - return base.AsyncCommandResult(command_name, - command_params, - bound_func).start() - return wrapper - return async_decorator diff --git a/ironic_python_agent/extensions/__init__.py b/ironic_python_agent/extensions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ironic_python_agent/base.py b/ironic_python_agent/extensions/base.py similarity index 87% rename from ironic_python_agent/base.py rename to ironic_python_agent/extensions/base.py index 8a7955352..54fde2c6e 100644 --- a/ironic_python_agent/base.py +++ b/ironic_python_agent/extensions/base.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import functools import threading import uuid @@ -192,3 +193,27 @@ class ExecuteCommandMixin(object): self.command_results[result.id] = result return result + + +def async_command(validator=None): + """Will run the command in an AsyncCommandResult in its own thread. + command_name is set based on the func name and command_params will + be whatever args/kwargs you pass into the decorated command. + """ + def async_decorator(func): + @functools.wraps(func) + def wrapper(self, command_name, **command_params): + # Run a validator before passing everything off to async. + # validators should raise exceptions or return silently. + if validator: + validator(self, **command_params) + + # bind self to func so that AsyncCommandResult doesn't need to + # know about the mode + bound_func = functools.partial(func, self) + + return AsyncCommandResult(command_name, + command_params, + bound_func).start() + return wrapper + return async_decorator diff --git a/ironic_python_agent/decom.py b/ironic_python_agent/extensions/decom.py similarity index 93% rename from ironic_python_agent/decom.py rename to ironic_python_agent/extensions/decom.py index 85a138d8e..6cd483fba 100644 --- a/ironic_python_agent/decom.py +++ b/ironic_python_agent/extensions/decom.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ironic_python_agent import base +from ironic_python_agent.extensions import base class DecomExtension(base.BaseAgentExtension): diff --git a/ironic_python_agent/flow.py b/ironic_python_agent/extensions/flow.py similarity index 94% rename from ironic_python_agent/flow.py rename to ironic_python_agent/extensions/flow.py index 9d4d43bae..617eebac5 100644 --- a/ironic_python_agent/flow.py +++ b/ironic_python_agent/extensions/flow.py @@ -14,9 +14,8 @@ from stevedore import enabled -from ironic_python_agent import base -from ironic_python_agent import decorators from ironic_python_agent import errors +from ironic_python_agent.extensions import base from ironic_python_agent.openstack.common import log LOG = log.getLogger(__name__) @@ -51,7 +50,7 @@ class FlowExtension(base.BaseAgentExtension, base.ExecuteCommandMixin): propagate_map_exceptions=True, ) - @decorators.async_command(_validate_exts) + @base.async_command(_validate_exts) def start_flow(self, command_name, flow=None): for task in flow: for method, params in task.items(): diff --git a/ironic_python_agent/standby.py b/ironic_python_agent/extensions/standby.py similarity index 96% rename from ironic_python_agent/standby.py rename to ironic_python_agent/extensions/standby.py index 133bcf718..a73086c8f 100644 --- a/ironic_python_agent/standby.py +++ b/ironic_python_agent/extensions/standby.py @@ -18,10 +18,9 @@ import requests import subprocess import time -from ironic_python_agent import base from ironic_python_agent import configdrive -from ironic_python_agent import decorators from ironic_python_agent import errors +from ironic_python_agent.extensions import base from ironic_python_agent import hardware from ironic_python_agent.openstack.common import log @@ -160,7 +159,7 @@ class StandbyExtension(base.BaseAgentExtension): self.cached_image_id = None - @decorators.async_command(_validate_image_info) + @base.async_command(_validate_image_info) def cache_image(self, command_name, image_info=None, force=False): device = hardware.get_manager().get_os_install_device() @@ -169,7 +168,7 @@ class StandbyExtension(base.BaseAgentExtension): _write_image(image_info, device) self.cached_image_id = image_info['id'] - @decorators.async_command(_validate_image_info) + @base.async_command(_validate_image_info) def prepare_image(self, command_name, image_info=None, @@ -188,7 +187,7 @@ class StandbyExtension(base.BaseAgentExtension): configdrive.write_configdrive(location, metadata, files) _copy_configdrive_to_disk(location, device) - @decorators.async_command() + @base.async_command() def run_image(self, command_name): script = _path_to_script('shell/reboot.sh') LOG.info('Rebooting system') diff --git a/ironic_python_agent/tests/agent.py b/ironic_python_agent/tests/agent.py index 680a8fb5c..49fa28f8b 100644 --- a/ironic_python_agent/tests/agent.py +++ b/ironic_python_agent/tests/agent.py @@ -22,10 +22,10 @@ import six from wsgiref import simple_server from ironic_python_agent import agent -from ironic_python_agent import base from ironic_python_agent.cmd import agent as agent_cmd from ironic_python_agent import encoding from ironic_python_agent import errors +from ironic_python_agent.extensions import base from ironic_python_agent import hardware EXPECTED_ERROR = RuntimeError('command execution failed') diff --git a/ironic_python_agent/tests/api.py b/ironic_python_agent/tests/api.py index c54932670..c762a7c6e 100644 --- a/ironic_python_agent/tests/api.py +++ b/ironic_python_agent/tests/api.py @@ -20,7 +20,7 @@ import pecan import pecan.testing from ironic_python_agent import agent -from ironic_python_agent import base +from ironic_python_agent.extensions import base PATH_PREFIX = '/v1' diff --git a/ironic_python_agent/tests/extensions/__init__.py b/ironic_python_agent/tests/extensions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ironic_python_agent/tests/base.py b/ironic_python_agent/tests/extensions/base.py similarity index 98% rename from ironic_python_agent/tests/base.py rename to ironic_python_agent/tests/extensions/base.py index 7b221e1b5..8eb915540 100644 --- a/ironic_python_agent/tests/base.py +++ b/ironic_python_agent/tests/extensions/base.py @@ -16,8 +16,8 @@ import mock from oslotest import base as test_base from stevedore import extension -from ironic_python_agent import base from ironic_python_agent import errors +from ironic_python_agent.extensions import base class FakeExtension(base.BaseAgentExtension): diff --git a/ironic_python_agent/tests/decom.py b/ironic_python_agent/tests/extensions/decom.py similarity index 94% rename from ironic_python_agent/tests/decom.py rename to ironic_python_agent/tests/extensions/decom.py index 8aa5a1a56..8d76c828d 100644 --- a/ironic_python_agent/tests/decom.py +++ b/ironic_python_agent/tests/extensions/decom.py @@ -14,7 +14,7 @@ from oslotest import base as test_base -from ironic_python_agent import decom +from ironic_python_agent.extensions import decom class TestDecomExtension(test_base.BaseTestCase): diff --git a/ironic_python_agent/tests/flow.py b/ironic_python_agent/tests/extensions/flow.py similarity index 96% rename from ironic_python_agent/tests/flow.py rename to ironic_python_agent/tests/extensions/flow.py index 5369b049d..d8b26ccba 100644 --- a/ironic_python_agent/tests/flow.py +++ b/ironic_python_agent/tests/extensions/flow.py @@ -19,10 +19,9 @@ from oslotest import base as test_base from stevedore import enabled from stevedore import extension -from ironic_python_agent import base -from ironic_python_agent import decorators from ironic_python_agent import errors -from ironic_python_agent import flow +from ironic_python_agent.extensions import base +from ironic_python_agent.extensions import flow FLOW_INFO = [ @@ -42,7 +41,7 @@ class FakeExtension(base.BaseAgentExtension): self.command_map['sleep'] = self.sleep self.command_map['sync_sleep'] = self.sync_sleep - @decorators.async_command() + @base.async_command() def sleep(self, command_name, sleep_info=None): time.sleep(sleep_info['time']) diff --git a/ironic_python_agent/tests/standby.py b/ironic_python_agent/tests/extensions/standby.py similarity index 93% rename from ironic_python_agent/tests/standby.py rename to ironic_python_agent/tests/extensions/standby.py index b8911ba01..739cf96bc 100644 --- a/ironic_python_agent/tests/standby.py +++ b/ironic_python_agent/tests/extensions/standby.py @@ -17,7 +17,7 @@ from oslotest import base as test_base import six from ironic_python_agent import errors -from ironic_python_agent import standby +from ironic_python_agent.extensions import standby if six.PY2: OPEN_FUNCTION_NAME = '__builtin__.open' @@ -182,7 +182,8 @@ class TestStandbyExtension(test_base.BaseTestCase): standby._download_image, image_info) - @mock.patch('ironic_python_agent.standby._verify_image', autospec=True) + @mock.patch('ironic_python_agent.extensions.standby._verify_image', + autospec=True) @mock.patch(OPEN_FUNCTION_NAME, autospec=True) @mock.patch('requests.get', autospec=True) def test_download_image_verify_fails(self, requests_mock, open_mock, @@ -224,8 +225,10 @@ class TestStandbyExtension(test_base.BaseTestCase): self.assertEqual(md5_mock.call_count, 1) @mock.patch('ironic_python_agent.hardware.get_manager', autospec=True) - @mock.patch('ironic_python_agent.standby._write_image', autospec=True) - @mock.patch('ironic_python_agent.standby._download_image', autospec=True) + @mock.patch('ironic_python_agent.extensions.standby._write_image', + autospec=True) + @mock.patch('ironic_python_agent.extensions.standby._download_image', + autospec=True) def test_cache_image(self, download_mock, write_mock, hardware_mock): image_info = self._build_fake_image_info() download_mock.return_value = None @@ -242,14 +245,18 @@ class TestStandbyExtension(test_base.BaseTestCase): self.assertEqual('SUCCEEDED', async_result.command_status) self.assertEqual(None, async_result.command_result) - @mock.patch('ironic_python_agent.standby._copy_configdrive_to_disk', + @mock.patch(('ironic_python_agent.extensions.standby.' + '_copy_configdrive_to_disk'), autospec=True) - @mock.patch('ironic_python_agent.standby.configdrive.write_configdrive', + @mock.patch(('ironic_python_agent.extensions.standby.configdrive.' + 'write_configdrive'), autospec=True) @mock.patch('ironic_python_agent.hardware.get_manager', autospec=True) - @mock.patch('ironic_python_agent.standby._write_image', autospec=True) - @mock.patch('ironic_python_agent.standby._download_image', autospec=True) - @mock.patch('ironic_python_agent.standby._configdrive_location', + @mock.patch('ironic_python_agent.extensions.standby._write_image', + autospec=True) + @mock.patch('ironic_python_agent.extensions.standby._download_image', + autospec=True) + @mock.patch('ironic_python_agent.extensions.standby._configdrive_location', autospec=True) def test_prepare_image(self, location_mock, diff --git a/setup.cfg b/setup.cfg index a5bf098a1..66a04b60b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,9 +19,9 @@ console_scripts = ironic-python-agent = ironic_python_agent.cmd.agent:run ironic_python_agent.extensions = - standby = ironic_python_agent.standby:StandbyExtension - decom = ironic_python_agent.decom:DecomExtension - flow = ironic_python_agent.flow:FlowExtension + standby = ironic_python_agent.extensions.standby:StandbyExtension + decom = ironic_python_agent.extensions.decom:DecomExtension + flow = ironic_python_agent.extensions.flow:FlowExtension ironic_python_agent.hardware_managers = generic = ironic_python_agent.hardware:GenericHardwareManager