Vars Module

70 views
Skip to first unread message

Kyle James Walker

unread,
Feb 21, 2014, 3:41:56 PM2/21/14
to ansible...@googlegroups.com
I have a fews questions:
1. Is there a way to turn on stdout for a task, to allow user prompting?
2. Is there support for creating a 'vars module', run before any tasks like vars and vars_prompt?
3. Any recommendations on another direction to get the desired results mentioned below?

I'm currently looking into creating a custom module that will read a file, take a user's input, and based on the input create a file on the system, with additional options like not prompting the user if the file already exists on the remote system.

The problem is I can't find a way to get the stdout to display when prompting the user, and it would be better to have a 'vars module' instead of 'task module' (run before the playbook, aka the same times vars_prompt is run).

I could do this with the vars_prompt, and a template, but then I loose flexibility and of the functionality currently in my app, and have more locations to update when changes are made.

Here's my example:
my_app/defaults.yaml (in revision control):  This has all the values that can be overridden, along with some defaults.  Some are perfectly fine to keep in revision control, so defaults are usually ok, others are not (passwords, etc).
# Program Defaults, do not edit this file!!!
# All values should be overridden in the 'settings.yaml' file.
sample_config:
  databases:
    database_name_1:
      user: user
      passwd: password
      host: db.example.com:5432
      db: postgres
      compress: true
      engine: postgresql
    database_name_2:
      user: user
      passwd: password
      db: example
      compress: true
      engine: postgresql
  secret_key: example hard to guess flask secret key
  more_values: more_examples

remote_location/settings.yaml: Generated by user with, outside revision control. This doesn't need every values, as I want the defaults left out of this file, so if updated in rev control they won't be overridden.
sample_config:
  databases:
    database_name_1:
      user: kyle_walker
      passwd: my password is secure
    database_name_2:
      user: sample_app_login
      passwd: the apps password is secure also
  secret_key: secure flask secret key

Core logic of my module (work in progress): This would work well enough if the stdout can be enabled.  But I think it would be better to be have has run at prompt time, and then use a copy with no source, and this variable data as the default contents, or another custom module.
#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import yaml

from collections import OrderedDict

DOCUMENTATION = '''
---
module: yaml_prompt
... Not needed for example ...
author: Kyle Walker
'''

EXAMPLES = '''
... Not needed for example ...
'''


def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
    class OrderedLoader(Loader):
        pass
    OrderedLoader.add_constructor(
        yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
        lambda loader, node: object_pairs_hook(loader.construct_pairs(node)))
    return yaml.load(stream, OrderedLoader)


def ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds):
    class OrderedDumper(Dumper):
        pass

    def _dict_representer(dumper, data):
        return dumper.represent_mapping(
            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
            data.items())

    OrderedDumper.add_representer(OrderedDict, _dict_representer)
    return yaml.dump(data, stream, OrderedDumper, **kwds)


def prompt_user(module, settings, depth=1, defaults=True):
    if depth is 1:
        print 'Enter values for:'

    for key, val in settings.iteritems():
        indent = "  " * depth
        if isinstance(val, dict):
            print "{}{}:".format(indent, key)

            org_len = len(val)
            prompt_user(module, val, depth+1, defaults)

            # Remove if all values were default.
            if defaults is False and len(val) is 0 and org_len is not 0:
                settings.pop(key)
        else:
            new_val = raw_input("{}{}({}):".format(indent, key, val))
            if new_val:
                settings[key] = new_val
            elif defaults is False:
                settings.pop(key)


def main():
    module = AnsibleModule(
        # not checking because of daisy chain to file module
        argument_spec=dict(
            src=dict(required=True),
            dest=dict(required=True),
            copy_defaults=dict(default=True, type='bool'),
            override=dict(default=True, type='bool'),
        ),
        add_file_common_args=True,
    )

    src = os.path.expanduser(module.params['src'])
    dest = os.path.expanduser(module.params['dest'])
    copy_defaults = module.params.get('copy_defaults', None)
    override = module.params.get('validate', None)

    settings = ordered_load(open(src), yaml.SafeLoader, OrderedDict)
    prompt_user(module, settings, defaults=copy_defaults)
    content = ordered_dump(settings,
                           Dumper=yaml.SafeDumper,
                           default_flow_style=False)

    module.fail_json(msg="Still testing, stop here.\n{}".format(content))

    res_args = dict(
        dest=dest,
        src=src,
        md5sum=md5sum_src,
        changed=changed
    )

    # Copy the created contents to the server.
    module.params['src'] = None
    module.params['dest'] = dest
    module.params['content'] = content
    file_args = module.load_file_common_arguments(module.params)
    res_args['changed'] = module.set_file_attributes_if_different(
        file_args,
        res_args['changed']
    )

    module.exit_json(**res_args)

# import module snippets
from ansible.module_utils.basic import *
main()

Michael DeHaan

unread,
Feb 21, 2014, 4:02:09 PM2/21/14
to ansible...@googlegroups.com
Since you're asking code questions, can you please repost this on ansible-devel ?

Thanks!




--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Kyle James Walker

unread,
Feb 21, 2014, 4:07:12 PM2/21/14
to ansible...@googlegroups.com
Thanks, I've just posted this in the correct group.

Reply all
Reply to author
Forward
0 new messages