# how to share custom plugins with the community

**URL:** <https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851>\
**Category:** Ansible Developer\
**Tags:** galaxy-ng\
**Created:** [March 12, 2016, 12:35pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851 "2016-03-12T12:35:15Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 12, 2016, 12:35pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/1 "2016-03-12T12:35:15Z")

</div>

Hello,

I’ve written a simple action plugin to import csv file data on the management machine into a variable in a playbook.

I’d like to be able to share this with the community but it seems that Ansible Galaxy is only for roles/modules which execute on the remote managed nodes.

[Note this is an action plugin **not** a role/module because the csv data is on the management machine, not the managed nodes.]

So, I have a few questions:

1. Am I correct that Ansible Galaxy cannot be used to share action plugins ?

2. If so, are there any plans for Ansible Galaxy to encompass other types of plugins besides roles/modules.

3. If not, what are the chances of this being included as an Ansible feature (I’d be happy to explain the use-case & work on a fork / pull request).

Regards,  
John

---

<div class="post-metadata">

**Author:** ![Brian\_Coca](https://avatars.discourse-cdn.com/v4/letter/b/6de8d8/32.png) [@Brian\_Coca](https://forum.ansible.com/u/Brian_Coca)\
**Post date:** [March 12, 2016, 8:02pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/2 "2016-03-12T20:02:28Z")

</div>

You can put your plugins in a role (inside plugin specific dir) and share that in galaxy, to use the plugin you just need to reference the role in a play.

---

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 13, 2016, 7:21am UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/3 "2016-03-13T07:21:54Z")

</div>

OK, I’ll try that. Thanks.

---

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 13, 2016, 6:06pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/4 "2016-03-13T18:06:48Z")

</div>

I’ve tried doing as you suggest but I’m getting the error:

ERROR! module is missing interpreter line

My action plugin is a single python script called load\_csv.py which I previously stored in ./action\_plugins, relative to the directory in which I have my playbook is stored.  
This worked.

Now I’ve moved it /etc/ansible/roles/load\_csv/action\_plugins and I’m getting the above error.

(/etc/ansible/roles/ is where ansible-galaxy installs roles on my system)

Looking at the ansible source around the error, it looks like it’s expecting to find some module code to go with the role (with a shebang on line 1).  
But surely this would run on the target node, which is not what I’m after.

Any ideas ?  
I’m new to the ansible source code so I’m finding it difficult to debug.

---

<div class="post-metadata">

**Author:** ![Mike\_Biancaniello](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@Mike\_Biancaniello](https://forum.ansible.com/u/Mike_Biancaniello)\
**Post date:** [March 14, 2016, 1:09pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/5 "2016-03-14T13:09:27Z")

</div>

I have found that if you use an action\_plugin, you still need a file of the same name in the library dir, even if that file is empty.

---

<div class="post-metadata">

**Author:** ![Timothy\_Appnel1](https://avatars.discourse-cdn.com/v4/letter/t/278dde/32.png) [@Timothy\_Appnel1](https://forum.ansible.com/u/Timothy_Appnel1)\
**Post date:** [March 14, 2016, 4:49pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/6 "2016-03-14T16:49:19Z")

</div>

Correct Mike.

John: Look at the modules for debug and assert. You’ll see they are just documentation. All the functionality of those modules happen in their associated action plugin.

[https://github.com/ansible/ansible-modules-core/blob/c86a0ef84a46133814bf6f240237640139e09fad/utilities/logic/assert.py](https://github.com/ansible/ansible-modules-core/blob/c86a0ef84a46133814bf6f240237640139e09fad/utilities/logic/assert.py)

[https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/action/assert.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/action/assert.py)

---

<div class="post-metadata">

**Author:** ![Timothy\_Appnel1](https://avatars.discourse-cdn.com/v4/letter/t/278dde/32.png) [@Timothy\_Appnel1](https://forum.ansible.com/u/Timothy_Appnel1)\
**Post date:** [March 14, 2016, 4:51pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/7 "2016-03-14T16:51:48Z")

</div>

The one caveat of Brian’s suggestion is that the community will have to “import” your plugin by declaring it as a role – one that will run no tasks, just put that plugin in Ansible’s search path for that play.

---

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 14, 2016, 9:46pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/8 "2016-03-14T21:46:38Z")

</div>

Mike, TImothy & Brian,

OK, got it now thanks.

Here’s my structure…

/etc/ansible/roles

> – load\_csv
> 
> > – action\_plugins  
> > `-- load_csv.py # action_plugin code `– library  
> > `-- load\_csv # empty

Called as follows from a playbook:

---

<div class="post-metadata">

**Author:** ![Mike\_Biancaniello](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@Mike\_Biancaniello](https://forum.ansible.com/u/Mike_Biancaniello)\
**Post date:** [March 14, 2016, 11:16pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/9 "2016-03-14T23:16:39Z")

</div>

I think, generally, action\_plugins are used if you want to do local pre-processing on the localhost before pushing changes to the remote server.

The other reason to use an action\_plugin is if your task needs access to `task vars` (different from task\_args) and other data that is not provided to the library modules. Remember that library modules don’t need to be python (I wrote one in TCL for something that required it).

For tasks that you only want to run once, Ansible provides a ‘run\_once’ attribute you can assign to the task. Personally, I don’t like the ‘run\_once’ because any changes are associated with the first server on your list (i.e the one for which the task was run once),so if it’s something that I just want run on the local server (e.g. open a new CM ticket to signify changes are starting), I usually create a play to run only on localhost and run that before the main one. In the CM example, I would also create a 3rd play at the end to close the ticket from localhost.

I agree that the library module vs action\_plugin is confusing and counter intuitive and hopefully, this will be clarified in a future release (e.g. eliminate the need for a library module when you already have an action\_plugin).

It might help to browse through many of the core module and also through any corresponding action\_plugins to better illustrate the use cases.

---

<div class="post-metadata">

**Author:** ![Timothy\_Appnel1](https://avatars.discourse-cdn.com/v4/letter/t/278dde/32.png) [@Timothy\_Appnel1](https://forum.ansible.com/u/Timothy_Appnel1)\
**Post date:** [March 15, 2016, 7:22pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/10 "2016-03-15T19:22:18Z")

</div>

run\_once is not the most elegant thing however you can some additional mileage out of it when used with local\_action.

(I didn’t have John’s cvs module to test with so I used the stat module to demonstrate the idea.)

---

<div class="post-metadata">

**Author:** ![Brian\_Coca](https://avatars.discourse-cdn.com/v4/letter/b/6de8d8/32.png) [@Brian\_Coca](https://forum.ansible.com/u/Brian_Coca)\
**Post date:** [March 15, 2016, 8:37pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/11 "2016-03-15T20:37:12Z")

</div>

action plugins need modules as these are then used to house your documentation.

---

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 15, 2016, 10:15pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/12 "2016-03-15T22:15:32Z")

</div>

Hi Timothy,

I thought include\_vars only understood YAML & JSON ??? That’s what the docs seem to suggest. If it supports CSV, then almost certainly it would be of use to me.

(The ultimate win I suppose would be for Ansible to support pluggable data sources.)

My CSV file contains filesystem data as a proof of concept:

> vg,lv,fstype,mount\_point,size,owner,perms  
> myvg,jb,ext3,/mnt/jb,20M,root,755  
> myvg,jb1,ext3,/mnt/jb/jb1,12M,root,755  
> myvg,jb2,ext3,/mnt/jb/jb2,32M,root,755

I have a module which loads the above data into a dict and iterates over it to creates logical volumes, filesystems and mounts.

The general idea behind this…

- My team carry out many system deployments.

- We are fed information from other teams as spreadsheets. E.g. lists of users/groups, software, filesystems, etc.

- If Ansible can consume data in CSV format, we can formalise the spreadsheets supplied by other teams to export CSV, to take a lot of the manual effort out of our deployments.

The organisation are currently using Puppet and could use Hiera to achieve a similar result. But they are open to suggestions, and I think Ansible might be a better fit for their way of working.

The module which uses the csv data is as follows:

**roles/filesystems/tasks/main.yml**

> ## `
> 
> # tasks file for filesystems roles
> 
> # 
> 
> - name: load csv file  
> load\_csv: file={{csvfile}}  
> register: filesystems
> 
> - name: create lvs  
> lvol: lv=“{{item.lv}}” vg=“{{item.vg}}” size=“{{item.size}}”  
> with\_items: “{{filesystems.data}}”
> 
> - name: create filesystems  
> filesystem: dev=“/dev/{{item.vg}}/{{item.lv}}” fstype=“{{item.fstype}}”  
> with\_items: “{{filesystems.data}}”
> 
> - name: create mounts  
> mount: src=“/dev/{{item.vg}}/{{item.lv}}” name=“{{item.mount\_point}}”  
> fstype=“{{item.fstype}}” state=mounted  
> with\_items: “{{filesystems.data}}”  
> `

The action plugin uses the header in line 1 of the CSV to create a dictionary.

**action\_plugins/load\_csv.py**

> `  
> from **future** import (absolute\_import, division, print\_function)  
> **metaclass** = type
> 
> from ansible.plugins.action import ActionBase
> 
> import csv  
> import os
> 
> class ActionModule(ActionBase):
> 
> def run(self, tmp=None, task\_vars=None):  
> if task\_vars is None:  
> task\_vars = dict()
> 
> super(ActionModule, self).run(tmp, task\_vars)
> 
> file = self.\_task.args.get(‘file’)
> 
> if file is None:  
> return dict(failed=True, msg=“file is required”)
> 
> try:  
> file = os.path.expanduser(file)  
> fobj = open(file, ‘rb’)  
> reader = csv.DictReader(fobj)  
> data = [i for i in reader]
> 
> except Exception as err:  
> return dict(failed=True, msg=“failed: %s” % err)
> 
> return dict(failed=False, changed=False, data=data,)  
> `

The top level playbook looks like this:

`

---

<div class="post-metadata">

**Author:** ![Timothy\_Appnel1](https://avatars.discourse-cdn.com/v4/letter/t/278dde/32.png) [@Timothy\_Appnel1](https://forum.ansible.com/u/Timothy_Appnel1)\
**Post date:** [March 15, 2016, 10:35pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/13 "2016-03-15T22:35:59Z")

</div>

Sorry if I wasn’t clear. I was referring to the include\_vars module as an alternative implementation of what you are doing in that it avoids the extra step of the required register to a variable. Instead place the cvs data could be returned and stored as facts. Given include\_vars behavior this seems more intuitive to me also.

---

<div class="post-metadata">

**Author:** ![James\_Cammarata](https://avatars.discourse-cdn.com/v4/letter/j/7c8e57/32.png) [@James\_Cammarata](https://forum.ansible.com/u/James_Cammarata)\
**Post date:** [March 16, 2016, 12:51am UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/14 "2016-03-16T00:51:04Z")

</div>

I believe a lookup plugin would be a better fit for loading a CSV file than a module or action plugin. Much easier to write and you can get and use the data in a single task rather than having to register a result and use it later.

---

<div class="post-metadata">

**Author:** ![Brian\_Coca](https://avatars.discourse-cdn.com/v4/letter/b/6de8d8/32.png) [@Brian\_Coca](https://forum.ansible.com/u/Brian_Coca)\
**Post date:** [March 16, 2016, 5:36am UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/15 "2016-03-16T05:36:05Z")

</div>

​csvfile lookup already exists​

---

<div class="post-metadata">

**Author:** ![James\_Cammarata](https://avatars.discourse-cdn.com/v4/letter/j/7c8e57/32.png) [@James\_Cammarata](https://forum.ansible.com/u/James_Cammarata)\
**Post date:** [March 16, 2016, 1:55pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/16 "2016-03-16T13:55:52Z")

</div>

Ahh, well, there you go.

---

<div class="post-metadata">

**Author:** ![John\_Buxton](https://avatars.discourse-cdn.com/v4/letter/j/7bcc69/32.png) [@John\_Buxton](https://forum.ansible.com/u/John_Buxton)\
**Post date:** [March 16, 2016, 2:31pm UTC](https://forum.ansible.com/t/how-to-share-custom-plugins-with-the-community/20851/17 "2016-03-16T14:31:42Z")

</div>

Hi,

csvfile lookup is only useful if you are looking up a known key so It’s no use in my situation.

I need to read the whole file and iterate through the results using with\_items.
