Add ara_record module to record key/value pairs
ara_record is a new Ansible module specific to ARA. It is meant for users to register any key value pair throughout their playbook execution so that it is available through CLI or the web interface. Change-Id: I480011a4037ecdc561183b7b7cb334011d1f4b10
This commit is contained in:
committed by
David Moreau Simard
parent
08f8e3d1b3
commit
f4353a941e
@@ -17,6 +17,7 @@ import os
|
||||
from ansible.constants import get_config, load_config_file
|
||||
|
||||
DEFAULT_ARA_DIR = os.path.expanduser('~/.ara')
|
||||
DEFAULT_ARA_TMPDIR = os.path.expanduser('~/.ansible/tmp')
|
||||
DEFAULT_DATABASE_PATH = os.path.join(DEFAULT_ARA_DIR, 'ansible.sqlite')
|
||||
DEFAULT_DATABASE = 'sqlite:///{}'.format(DEFAULT_DATABASE_PATH)
|
||||
DEFAULT_ARA_LOGFILE = os.path.join(DEFAULT_ARA_DIR, 'ara.log')
|
||||
@@ -31,6 +32,9 @@ config, path = load_config_file()
|
||||
ARA_DIR = get_config(
|
||||
config, 'ara', 'dir', 'ARA_DIR',
|
||||
DEFAULT_ARA_DIR)
|
||||
ARA_TMP_DIR = get_config(
|
||||
config, 'defaults', 'local_tmp', 'ANSIBLE_LOCAL_TEMP',
|
||||
DEFAULT_ARA_TMPDIR, istmppath=True)
|
||||
ARA_LOG_FILE = get_config(
|
||||
config, 'ara', 'logfile', 'ARA_LOG_FILE',
|
||||
DEFAULT_ARA_LOGFILE)
|
||||
|
||||
32
ara/db/versions/e8e78fd08bf2_ara_record_data.py
Normal file
32
ara/db/versions/e8e78fd08bf2_ara_record_data.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""ara_record data
|
||||
|
||||
Revision ID: e8e78fd08bf2
|
||||
Revises: da9459a1f71c
|
||||
Create Date: 2016-11-01 18:02:38.685998
|
||||
|
||||
"""
|
||||
# flake8: noqa
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e8e78fd08bf2'
|
||||
down_revision = 'da9459a1f71c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('data',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('playbook_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('key', sa.String(length=255), nullable=True),
|
||||
sa.Column('value', sa.Text(length=16777215), nullable=True),
|
||||
sa.ForeignKeyConstraint(['playbook_id'], ['playbooks.id'], ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('playbook_id', 'key')
|
||||
)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('data')
|
||||
### end Alembic commands ###
|
||||
@@ -124,6 +124,7 @@ class Playbook(db.Model, TimedEntity):
|
||||
|
||||
`Playbook` entities have the following relationships:
|
||||
|
||||
- `data` -- a list of k/v pairs recorded in this playbook run.
|
||||
- `plays` -- a list of plays encountered in this playbook run.
|
||||
- `tasks` -- a list of tasks encountered in this playbook run.
|
||||
- `stats` -- a list of statistic records, one for each host
|
||||
@@ -137,6 +138,7 @@ class Playbook(db.Model, TimedEntity):
|
||||
|
||||
id = std_pkey()
|
||||
path = db.Column(db.Text)
|
||||
data = one_to_many('Data', backref='playbook')
|
||||
files = one_to_many('File', backref='playbook')
|
||||
plays = one_to_many('Play', backref='playbook')
|
||||
tasks = one_to_many('Task', backref='playbook')
|
||||
@@ -387,3 +389,26 @@ class Stats(db.Model):
|
||||
|
||||
def __repr__(self):
|
||||
return '<Stats for %s>' % self.host.name
|
||||
|
||||
|
||||
class Data(db.Model):
|
||||
'''The `Data` object represents a recorded key/value pair provided by
|
||||
the ara_record module.
|
||||
|
||||
A `Data` entity has the following relationships:
|
||||
|
||||
- `playbook` -- the playbook this key/value pair was recorded in
|
||||
'''
|
||||
|
||||
__tablename__ = 'data'
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint('playbook_id', 'key'),
|
||||
)
|
||||
|
||||
id = std_pkey()
|
||||
playbook_id = std_fkey('playbooks.id')
|
||||
key = db.Column(db.String(255))
|
||||
value = db.Column(db.Text(16777215))
|
||||
|
||||
def __repr__(self):
|
||||
return '<Data %s:%s>' % (self.data.playbook_id, self.data.key)
|
||||
|
||||
0
ara/plugins/actions/__init__.py
Normal file
0
ara/plugins/actions/__init__.py
Normal file
133
ara/plugins/actions/ara_record.py
Normal file
133
ara/plugins/actions/ara_record.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# Copyright Red Hat, Inc. All Rights Reserved.
|
||||
#
|
||||
# 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 json
|
||||
import os
|
||||
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
||||
try:
|
||||
from ara import app, models
|
||||
from ara.models import db
|
||||
HAS_ARA = True
|
||||
except ImportError:
|
||||
HAS_ARA = False
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ara_record
|
||||
short_description: Ansible module to record persistent data with ARA.
|
||||
version_added: "2.0"
|
||||
author: "RDO Community <rdo-list@redhat.com>"
|
||||
description:
|
||||
- Ansible module to record persistent data with ARA.
|
||||
options:
|
||||
key:
|
||||
description:
|
||||
- Name of the key to write data to
|
||||
required: true
|
||||
value:
|
||||
description:
|
||||
- Value of the key written to
|
||||
required: true
|
||||
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "ara >= 0.10.0"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Write static data
|
||||
- ara_record:
|
||||
key: "foo"
|
||||
value: "bar"
|
||||
|
||||
# Write dynamic data
|
||||
- shell: cd dev && git rev-parse HEAD
|
||||
register: git_version
|
||||
delegate_to: localhost
|
||||
|
||||
- ara_record:
|
||||
key: "git_version"
|
||||
value: "{{ git_version.stdout }}"
|
||||
'''
|
||||
|
||||
|
||||
class ActionModule(ActionBase):
|
||||
''' Record persistent data as key/value pairs in ARA '''
|
||||
|
||||
TRANSFERS_FILES = False
|
||||
VALID_ARGS = frozenset(('key', 'value'))
|
||||
|
||||
def create_or_update_key(self, playbook_id, key, value):
|
||||
try:
|
||||
data = (models.Data.query
|
||||
.filter_by(key=key)
|
||||
.filter_by(playbook_id=playbook_id)
|
||||
.one())
|
||||
data.value = value
|
||||
except models.NoResultFound:
|
||||
data = models.Data(playbook_id=playbook_id,
|
||||
key=key,
|
||||
value=value)
|
||||
db.session.add(data)
|
||||
db.session.commit()
|
||||
|
||||
return data
|
||||
|
||||
def run(self, tmp=None, task_vars=None):
|
||||
if task_vars is None:
|
||||
task_vars = dict()
|
||||
|
||||
if not HAS_ARA:
|
||||
result = {
|
||||
"failed": True,
|
||||
"msg": "ARA is required to run this module."
|
||||
}
|
||||
return result
|
||||
|
||||
for arg in self._task.args:
|
||||
if arg not in self.VALID_ARGS:
|
||||
result = {
|
||||
"failed": True,
|
||||
"msg": "'{0}' is not a valid option.".format(arg)
|
||||
}
|
||||
return result
|
||||
|
||||
result = super(ActionModule, self).run(tmp, task_vars)
|
||||
|
||||
key = self._task.args.get('key', None)
|
||||
value = self._task.args.get('value', None)
|
||||
|
||||
required = ['key', 'value']
|
||||
for parameter in required:
|
||||
if not self._task.args.get(parameter):
|
||||
result['failed'] = True
|
||||
result['msg'] = "{} parameter is required".format(parameter)
|
||||
return result
|
||||
|
||||
# Retrieve the persisted playbook_id from tmpfile
|
||||
tmpfile = os.path.join(app.config['ARA_TMP_DIR'], 'ara.json')
|
||||
with open(tmpfile) as file:
|
||||
data = json.load(file)
|
||||
playbook_id = data['playbook']['id']
|
||||
|
||||
try:
|
||||
self.create_or_update_key(playbook_id, key, value)
|
||||
result['msg'] = "Data recorded in ARA for this playbook."
|
||||
except Exception as e:
|
||||
result['failed'] = True
|
||||
result['msg'] = "Data not recorded in ARA: {0}".format(str(e))
|
||||
return result
|
||||
@@ -263,6 +263,16 @@ class CallbackModule(CallbackBase):
|
||||
file_ = self.get_or_create_file(path)
|
||||
file_.is_playbook = True
|
||||
|
||||
# We need to persist the playbook id so it can be used by the modules
|
||||
data = {
|
||||
'playbook': {
|
||||
'id': self.playbook.id
|
||||
}
|
||||
}
|
||||
tmpfile = os.path.join(app.config['ARA_TMP_DIR'], 'ara.json')
|
||||
with open(tmpfile, 'w') as file:
|
||||
file.write(json.dumps(data))
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
self.close_task()
|
||||
self.close_play()
|
||||
|
||||
0
ara/plugins/modules/__init__.py
Normal file
0
ara/plugins/modules/__init__.py
Normal file
60
ara/plugins/modules/ara_record.py
Normal file
60
ara/plugins/modules/ara_record.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# Copyright Red Hat, Inc. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# This file is purposefully left empty due to an Ansible issue
|
||||
# Details at: https://github.com/ansible/ansible/pull/18208
|
||||
|
||||
# TODO: Remove this file and update the documentation when the issue is fixed,
|
||||
# released and present in all supported versions.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ara_record
|
||||
short_description: Ansible module to record data with ARA
|
||||
version_added: "2.0"
|
||||
author: "RDO Community <rdo-list@redhat.com>"
|
||||
description:
|
||||
- Ansible module to record data with ARA. This module should always be
|
||||
executed wherever the playbook is run from.
|
||||
options:
|
||||
key:
|
||||
description:
|
||||
- Name of the key to write data to
|
||||
required: true
|
||||
value:
|
||||
description:
|
||||
- Value of the key written to
|
||||
required: true
|
||||
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "ara >= 0.10.0"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Write static data
|
||||
- ara_record:
|
||||
key: "foo"
|
||||
value: "bar"
|
||||
|
||||
# Write dynamic data
|
||||
- shell: cd dev && git rev-parse HEAD
|
||||
register: git_version
|
||||
delegate_to: localhost
|
||||
|
||||
- ara_record:
|
||||
key: "git_version"
|
||||
value: "{{ git_version.stdout }}"
|
||||
'''
|
||||
@@ -20,6 +20,9 @@ an example that covers most common locations:
|
||||
|
||||
[defaults]
|
||||
callback_plugins = /usr/lib/python2.7/site-packages/ara/plugins/callbacks:$VIRTUAL_ENV/lib/python2.7/site-packages/ara/plugins/callbacks:/usr/local/lib/python2.7/dist-packages/ara/plugins/callbacks
|
||||
# If you'd like to use the ara_record module, you'll also need the following:
|
||||
action_plugins = /usr/lib/python2.7/site-packages/ara/plugins/actions:$VIRTUAL_ENV/lib/python2.7/site-packages/ara/plugins/actions:/usr/local/lib/python2.7/dist-packages/ara/plugins/actions
|
||||
library = /usr/lib/python2.7/site-packages/ara/plugins/modules:$VIRTUAL_ENV/lib/python2.7/site-packages/ara/plugins/modules:/usr/local/lib/python2.7/dist-packages/ara/plugins/modules
|
||||
|
||||
.. _callback: https://github.com/openstack/ara/blob/master/ara/plugins/callbacks/log_ara.py
|
||||
.. _ansible.cfg: http://docs.ansible.com/ansible/intro_configuration.html#configuration-file
|
||||
|
||||
@@ -13,6 +13,32 @@ in the ``callback_plugins`` Ansible configuration.
|
||||
After running an Ansible playbook, the database will be created if it doesn't
|
||||
exist and will be used automatically.
|
||||
|
||||
Using the ara_record module
|
||||
---------------------------
|
||||
ARA comes with a built-in module called ``ara_record``.
|
||||
|
||||
This module can be used as an action for a task in your Ansible playbooks in
|
||||
order to register whatever you'd like in a key/value format, for example::
|
||||
|
||||
- name: Test playbook
|
||||
hosts: all
|
||||
gather_facts: yes
|
||||
tasks:
|
||||
- name: Get git revision of playbooks
|
||||
command: git rev-parse HEAD
|
||||
register: git_version
|
||||
|
||||
- name: Record git revision
|
||||
ara_record:
|
||||
key: "git_revision"
|
||||
value: "{{ git_version.stdout }}"
|
||||
|
||||
This data will be recorded inside ARA's database and associated with the
|
||||
particular playbook run that was executed.
|
||||
|
||||
You can then query ARA, either through the CLI or the web interface to see the
|
||||
recorded values.
|
||||
|
||||
Looking at the data
|
||||
-------------------
|
||||
Once you've run ansible-playbook at least once, the database will be populated
|
||||
|
||||
Reference in New Issue
Block a user