137 lines
4.6 KiB
Python
Raw Normal View History

# 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.
2013-12-17 15:13:30 -08:00
import threading
2014-01-02 13:17:15 -08:00
import uuid
2013-12-17 15:13:30 -08:00
import six
2013-12-17 15:13:30 -08:00
from ironic_python_agent import encoding
from ironic_python_agent import errors
from ironic_python_agent.openstack.common import log
2013-12-17 15:13:30 -08:00
2014-01-02 13:17:15 -08:00
class AgentCommandStatus(object):
2014-03-12 09:50:32 -07:00
RUNNING = u'RUNNING'
SUCCEEDED = u'SUCCEEDED'
FAILED = u'FAILED'
2014-01-02 13:17:15 -08:00
class BaseCommandResult(encoding.Serializable):
def __init__(self, command_name, command_params):
self.id = six.text_type(uuid.uuid4())
2014-01-02 13:17:15 -08:00
self.command_name = command_name
self.command_params = command_params
self.command_status = AgentCommandStatus.RUNNING
self.command_error = None
self.command_result = None
def serialize(self):
2014-03-12 09:50:32 -07:00
return dict((
(u'id', self.id),
(u'command_name', self.command_name),
(u'command_params', self.command_params),
(u'command_status', self.command_status),
(u'command_error', self.command_error),
(u'command_result', self.command_result),
))
2014-01-02 13:17:15 -08:00
def is_done(self):
return self.command_status != AgentCommandStatus.RUNNING
2014-01-09 15:35:35 -08:00
def join(self):
return self
2014-01-02 13:17:15 -08:00
class SyncCommandResult(BaseCommandResult):
def __init__(self, command_name, command_params, success, result_or_error):
super(SyncCommandResult, self).__init__(command_name,
command_params)
if success:
self.command_status = AgentCommandStatus.SUCCEEDED
self.command_result = result_or_error
else:
self.command_status = AgentCommandStatus.FAILED
self.command_error = result_or_error
class AsyncCommandResult(BaseCommandResult):
"""A command that executes asynchronously in the background.
:param execute_method: a callable to be executed asynchronously
"""
def __init__(self, command_name, command_params, execute_method):
super(AsyncCommandResult, self).__init__(command_name, command_params)
self.execute_method = execute_method
self.command_state_lock = threading.Lock()
thread_name = 'agent-command-{0}'.format(self.id)
self.execution_thread = threading.Thread(target=self.run,
name=thread_name)
def serialize(self):
with self.command_state_lock:
return super(AsyncCommandResult, self).serialize()
def start(self):
self.execution_thread.start()
return self
def join(self, timeout=None):
self.execution_thread.join(timeout)
return self
def is_done(self):
with self.command_state_lock:
return super(AsyncCommandResult, self).is_done()
def run(self):
try:
result = self.execute_method(self.command_name,
**self.command_params)
with self.command_state_lock:
self.command_result = result
self.command_status = AgentCommandStatus.SUCCEEDED
except Exception as e:
if not isinstance(e, errors.RESTError):
e = errors.CommandExecutionError(str(e))
with self.command_state_lock:
self.command_error = e
self.command_status = AgentCommandStatus.FAILED
class BaseAgentExtension(object):
2014-01-14 21:27:23 -08:00
def __init__(self, name):
super(BaseAgentExtension, self).__init__()
2014-03-17 15:17:27 -07:00
self.log = log.getLogger(__name__)
2014-01-14 21:27:23 -08:00
self.name = name
2014-01-21 10:42:43 -08:00
self.command_map = {}
def execute(self, command_name, **kwargs):
if command_name not in self.command_map:
raise errors.InvalidCommandError(
'Unknown command: {0}'.format(command_name))
2014-01-21 10:42:43 -08:00
result = self.command_map[command_name](command_name, **kwargs)
# In order to enable extremely succinct synchronous commands, we allow
# them to return a value directly, and we'll handle wrapping it up in a
# SyncCommandResult
if not isinstance(result, BaseCommandResult):
result = SyncCommandResult(command_name, kwargs, True, result)
return result