Skip to content

RFR: Add actions test cases pass #1 #507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 56 additions & 41 deletions st2actions/st2actions/bootstrap/actionsregistrar.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,82 +8,97 @@
from st2common import log as logging
from st2common.content.loader import ContentPackLoader
from st2common.content.requirementsvalidator import RequirementsValidator
from st2common.exceptions.db import StackStormDBObjectNotFoundError
from st2common.persistence.action import Action
from st2common.models.db.action import ActionDB
from st2common.util.action_db import get_runnertype_by_name

LOG = logging.getLogger(__name__)


def _get_actions_from_pack(pack):
return glob.glob(pack + '/*.json')
class ActionsRegistrar(object):
def _get_actions_from_pack(self, pack):
return glob.glob(pack + '/*.json')


def _register_actions_from_pack(pack, actions):
for action in actions:
LOG.debug('Loading action from %s.', action)
def _register_action(self, pack, action):
with open(action, 'r') as fd:
try:
content = json.load(fd)
except ValueError:
LOG.exception('Unable to load action from %s.', action)
continue
LOG.exception('Failed loading action json from %s.', action)
raise

try:
model = Action.get_by_name(str(content['name']))
except ValueError:
model = ActionDB()
model.name = content['name']
model.description = content['description']
model.enabled = content['enabled']
pack_name = content.get('pack', None)
pack_name = content.get('content_pack', None)
if not pack_name or pack_name == '':
pack_name = pack
LOG.warning('Pack name missing. Using: %s', pack_name)
model.content_pack = pack_name
model.entry_point = content['entry_point']
model.parameters = content.get('parameters', {})
model.required_parameters = content.get('required_parameters', [])
try:
runner_type = get_runnertype_by_name(str(content['runner_type']))
model.runner_type = {'name': runner_type.name}
except StackStormDBObjectNotFoundError:
LOG.exception('Failed to register action %s as runner %s was not found',
model.name, str(content['runner_type']))
continue
runner_type = str(content['runner_type'])
valid_runner_type, runner_type_db = self._has_valid_runner_type(runner_type)
if valid_runner_type:
model.runner_type = {'name': runner_type_db.name}
else:
LOG.exception('Runner type %s doesn\'t exist.')
raise

try:
model = Action.add_or_update(model)
LOG.audit('Action created. Action %s from %s.', model, action)
except Exception:
LOG.exception('Failed to create action %s.', model.name)
LOG.exception('Failed to write action to db %s.', model.name)
raise

def _has_valid_runner_type(self, runner_type):
try:
return True, get_runnertype_by_name(runner_type)
except:
return False, None

# XXX: Requirements for actions is tricky because actions can execute remotely.
# Currently, this method is unused.
def _is_requirements_ok(actions_dir):
rqmnts_file = os.path.join(actions_dir, 'requirements.txt')
def _register_actions_from_pack(self, pack, actions):
for action in actions:
try:
LOG.debug('Loading action from %s.', action)
self._register_action(pack, action)
except Exception:
LOG.exception('Unable to register action: %s', action)
continue

if not os.path.exists(rqmnts_file):
return True
# XXX: Requirements for actions is tricky because actions can execute remotely.
# Currently, this method is unused.
def _is_requirements_ok(self, actions_dir):
rqmnts_file = os.path.join(actions_dir, 'requirements.txt')

missing = RequirementsValidator.validate(rqmnts_file)
if missing:
LOG.warning('Actions in %s missing dependencies: %s', actions_dir, ','.join(missing))
return False
return True
if not os.path.exists(rqmnts_file):
return True

missing = RequirementsValidator.validate(rqmnts_file)
if missing:
LOG.warning('Actions in %s missing dependencies: %s', actions_dir, ','.join(missing))
return False
return True

def _register_actions_from_packs(base_dir):
pack_loader = ContentPackLoader()
dirs = pack_loader.get_content(base_dir=base_dir,
content_type='actions')
for pack, actions_dir in six.iteritems(dirs):
try:
actions = _get_actions_from_pack(actions_dir)
_register_actions_from_pack(pack, actions)
except:
LOG.exception('Failed registering all actions from pack: %s', actions_dir)
def register_actions_from_packs(self, base_dir):
pack_loader = ContentPackLoader()
dirs = pack_loader.get_content(base_dir=base_dir,
content_type='actions')
for pack, actions_dir in six.iteritems(dirs):
try:
actions = self._get_actions_from_pack(actions_dir)
self._register_actions_from_pack(pack, actions)
except:
LOG.exception('Failed registering all actions from pack: %s', actions_dir)


def register_actions():
return _register_actions_from_packs(cfg.CONF.content.content_packs_base_path)
def register_actions(content_packs_base_path=None):
if not content_packs_base_path:
content_packs_base_path = cfg.CONF.content.content_packs_base_path
return ActionsRegistrar().register_actions_from_packs(content_packs_base_path)
159 changes: 0 additions & 159 deletions st2actions/st2actions/container/__init__.py
Original file line number Diff line number Diff line change
@@ -1,159 +0,0 @@
import importlib

from st2common import log as logging
from st2common.exceptions.actionrunner import ActionRunnerCreateError
from st2common.models.api.action import (ACTIONEXEC_STATUS_COMPLETE,
ACTIONEXEC_STATUS_ERROR)

from st2common.util.action_db import (update_actionexecution_status, get_actionexec_by_id)

from st2actions.container import actionsensor
from st2actions.container.service import (RunnerContainerService)
import six


LOG = logging.getLogger(__name__)


class RunnerContainer():

def __init__(self):
LOG.info('Action RunnerContainer instantiated.')
self._pending = []

def _get_runner(self, runnertype_db):
"""
Load the module specified by the runnertype_db.runner_module field and
return an instance of the runner.
"""

module_name = runnertype_db.runner_module
LOG.debug('Runner loading python module: %s', module_name)
try:
module = importlib.import_module(module_name, package=None)
except Exception as e:
LOG.exception('Failed to import module %s.', module_name)
raise ActionRunnerCreateError(e)

LOG.debug('Instance of runner module: %s', module)

runner = module.get_runner()
LOG.debug('Instance of runner: %s', runner)
return runner

def dispatch(self, liveaction_db, runnertype_db, action_db, actionexec_db):

runner_type = runnertype_db.name

LOG.info('Dispatching runner for Live Action "%s"', liveaction_db)
LOG.debug(' liverunner_type: %s', runner_type)
LOG.debug(' RunnerType: %s', runnertype_db)
LOG.debug(' ActionExecution: %s', actionexec_db)

# Get runner instance.
runner = self._get_runner(runnertype_db)
LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner)

# Invoke pre_run, run, post_run cycle.
result = self._do_run(liveaction_db.id, runner, runnertype_db, action_db, actionexec_db)
LOG.debug('runner do_run result: %s', result)

actionsensor.post_trigger(actionexec_db)
LOG.audit('ActionExecution complete. liveaction_id="%s" resulted in '
'actionexecution_db="%s"', liveaction_db.id, actionexec_db)

return result

def _do_run(self, liveaction_id, runner, runnertype_db, action_db, actionexec_db):
# Runner parameters should use the defaults from the RunnerType object.
# The runner parameter defaults may be overridden by values provided in
# the Action and ActionExecution.
actionexec_runner_parameters, actionexec_action_parameters = RunnerContainer._split_params(
runnertype_db, action_db, actionexec_db)
action_action_parameters = dict(action_db.parameters)

# Create runner parameter by merging default values with dynamic values
runner_parameters = {k: v['default'] if 'default' in v else None
for k, v in six.iteritems(runnertype_db.runner_parameters)}

# pick overrides from action_action_parameters & actionexec_runner_parameters
for param in runner_parameters:
# values from actionexec_runner_parameters override action_action_parameters.
if param in actionexec_runner_parameters:
runner_parameters[param] = actionexec_runner_parameters[param]
continue
if param in action_action_parameters and 'default' in action_action_parameters[param]:
runner_parameters[param] = action_action_parameters[param]['default']

# Create action parameters by merging default values with dynamic values
action_parameters = {k: v['default'] if 'default' in v else None
for k, v in six.iteritems(action_db.parameters)}

# pick overrides from actionexec_action_parameters
for param in action_parameters:
if param in actionexec_action_parameters:
action_parameters[param] = actionexec_action_parameters[param]

runner.liveaction_id = liveaction_id
runner.container_service = RunnerContainerService(self)
runner.action = action_db
runner.action_name = action_db.name
runner.action_execution_id = str(actionexec_db.id)
runner.entry_point = action_db.entry_point
runner.runner_parameters = runner_parameters
runner.context = getattr(actionexec_db, 'context', dict())
runner.callback = getattr(actionexec_db, 'callback', dict())

LOG.debug('Performing pre-run for runner: %s', runner)
runner.pre_run()

LOG.debug('Performing run for runner: %s', runner)
run_result = runner.run(action_parameters)
LOG.debug('Result of run: %s', run_result)

# Re-load Action Execution from DB:
actionexec_db = get_actionexec_by_id(actionexec_db.id)

# TODO: Store payload when DB model can hold payload data
action_result = runner.container_service.get_result()
actionexec_status = runner.container_service.get_status()
LOG.debug('Result as reporter to container service: %s', action_result)

if action_result is None:
# If the runner didn't set an exit code then the liveaction didn't complete.
# Therefore, the liveaction produced an error.
result = False
if not actionexec_status:
actionexec_status = ACTIONEXEC_STATUS_ERROR
runner.container_service.report_status(actionexec_status)
else:
# So long as the runner produced an exit code, we can assume that the
# Live Action ran to completion.
result = True
actionexec_db.result = action_result
if not actionexec_status:
actionexec_status = ACTIONEXEC_STATUS_COMPLETE
runner.container_service.report_status(actionexec_status)

# Push result data and updated status to ActionExecution DB
update_actionexecution_status(actionexec_status, actionexec_db=actionexec_db)

LOG.debug('Performing post_run for runner: %s', runner)
runner.post_run()
runner.container_service = None

return result

@staticmethod
def _split_params(runnertype_db, action_db, actionexec_db):
return (
{param: actionexec_db.parameters[param]
for param in runnertype_db.runner_parameters if param in actionexec_db.parameters},

{param: actionexec_db.parameters[param]
for param in action_db.parameters if param in actionexec_db.parameters}
)


def get_runner_container():
return RunnerContainer()
Loading