# Developing a vars\_plugin

**URL:** <https://forum.ansible.com/t/developing-a-vars-plugin/16182>\
**Category:** Ansible Project\
**Created:** [September 11, 2014, 3:27pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182 "2014-09-11T15:27:00Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Guy\_Matz1](https://avatars.discourse-cdn.com/v4/letter/g/b5e925/32.png) [@Guy\_Matz1](https://forum.ansible.com/u/Guy_Matz1)\
**Post date:** [September 11, 2014, 3:27pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/1 "2014-09-11T15:27:00Z")

</div>

Hi! Anyone have any advice on developing a vars\_plugin? This may sound kinda screwy, but for a chef → ansible transition I need to access data from a chef data\_bag in an ansible play . . . I’m able to get the chef data using pychef, but I’m having issues when trying to run the code within vars\_plugin code:

import chef

def get\_dbag\_data():  
chef\_api = chef.autoconfigure()  
dbag, dbag\_item = ‘dbag’, ‘dbag\_item’  
bag = chef.DataBag(dbag) # some debugging shows the problem is here on the second run. . !  
item = bag[dbag\_item]  
return item.to\_dict()

The code above works outside of ansible, but when run in ansible it seems to get run twice, and fails on the “bag = chef.DataBag(dbag)” line the second time around.

Any thoughts would be greatly appreciated!

Thanks!  
Guy

---

<div class="post-metadata">

**Author:** ![sivel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/sivel/32/1198_2.png) [@sivel](https://forum.ansible.com/u/sivel)\
**Post date:** [September 11, 2014, 3:40pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/2 "2014-09-11T15:40:27Z")

</div>

Perhaps you could show your vars\_plugin? There is the potential that it could be run twice, depending on where you call that code from.

The 3 following methods are all run 1 time in ansible.inventory.Inventory:

plugin.get\_group\_vars  
plugin.run  
plugin.get\_host\_vars

Also, as a heads up, this is probably a better discussion for ansible-devel.

---

<div class="post-metadata">

**Author:** ![Guy\_Matz1](https://avatars.discourse-cdn.com/v4/letter/g/b5e925/32.png) [@Guy\_Matz1](https://forum.ansible.com/u/Guy_Matz1)\
**Post date:** [September 11, 2014, 4:11pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/3 "2014-09-11T16:11:57Z")

</div>

Matt,  
Thanks for the reply! Here’s the full code

import chef

def get\_dbag\_data():  
chef\_api = chef.autoconfigure()  
dbag, dbag\_item = ‘twelve\_factor\_environment’, ‘scratch-gmatz-sandbox-app-web’  
bag = chef.DataBag(dbag)  
item = bag[dbag\_item]  
return item.to\_dict()

class VarsModule(object):  
def **init** (self, inventory):  
self.inventory = inventory  
self.group\_cache = {}  
def run(self, host, vault\_password=None):  
inventory = self.inventory  
results = get\_dbag\_data()  
return results

Thanks!

---

<div class="post-metadata">

**Author:** ![Guy\_Matz](https://avatars.discourse-cdn.com/v4/letter/g/57b2e6/32.png) [@Guy\_Matz](https://forum.ansible.com/u/Guy_Matz)\
**Post date:** [September 11, 2014, 4:12pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/4 "2014-09-11T16:12:30Z")

</div>

migrated to ansible-devel . . . thanks!

---

<div class="post-metadata">

**Author:** ![Serge\_van\_Ginderacht](https://avatars.discourse-cdn.com/v4/letter/s/b782af/32.png) [@Serge\_van\_Ginderacht](https://forum.ansible.com/u/Serge_van_Ginderacht)\
**Post date:** [September 11, 2014, 5:40pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/5 "2014-09-11T17:40:04Z")

</div>

​Please note that run() was kept around for backwards compatibility. Before  
1.7, this was the only exposed function in vars\_plugins​, and it was  
supposed to return \*all\* calculated vars for a particular host.

See lib/ansible/inventory/vars\_plugins/noop.py for the signatures.

For newly written plugins, just use get\_group/host\_vars. Those are called  
just once for every group/host.

---

<div class="post-metadata">

**Author:** ![Serge\_van\_Ginderacht](https://avatars.discourse-cdn.com/v4/letter/s/b782af/32.png) [@Serge\_van\_Ginderacht](https://forum.ansible.com/u/Serge_van_Ginderacht)\
**Post date:** [September 11, 2014, 5:51pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/6 "2014-09-11T17:51:16Z")

</div>

​Hi Guy,  
​

> import chef
> 
> def get\_dbag\_data():  
> &nbsp;&nbsp;&nbsp;&nbsp;chef\_api = chef.autoconfigure()  
> &nbsp;&nbsp;&nbsp;&nbsp;dbag, dbag\_item = 'twelve\_factor\_environment',  
> 'scratch-gmatz-sandbox-app-web'  
> &nbsp;&nbsp;&nbsp;&nbsp;bag = chef.DataBag(dbag)  
> &nbsp;&nbsp;&nbsp;&nbsp;item = bag[dbag\_item]  
> &nbsp;&nbsp;&nbsp;&nbsp;return item.to\_dict()
> 
> class VarsModule(object):  
> &nbsp;&nbsp;&nbsp;&nbsp;def \_\_init\_\_(self, inventory):  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.inventory = inventory  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.group\_cache = {}  
> &nbsp;&nbsp;&nbsp;&nbsp;def run(self, host, vault\_password=None):  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inventory = self.inventory  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results = get\_dbag\_data()  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return results

​So, you are using the run() function here, which gets a host as a  
parameter​ for which you are supposed tro return the its vars.

I have no clue what the chef code does, but this sounds like it always  
returns data for that sandbox web host?  
How does ""bag = chef.DataBag(dbag)"" fail at the second run?  
(It runs - at least twice - so you have - at least - two hosts in your  
inventory).

The code in the VarsModule constructor is not used, also the inventory var  
in run() - you might leave those out.

You'll need to give me some more info on that chef code, what it does, and  
what exactly you try to retrieve, and how you expect to put this in ansible.

&nbsp;&nbsp;Serge

---

<div class="post-metadata">

**Author:** ![Guy\_Matz1](https://avatars.discourse-cdn.com/v4/letter/g/b5e925/32.png) [@Guy\_Matz1](https://forum.ansible.com/u/Guy_Matz1)\
**Post date:** [September 11, 2014, 7:59pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/7 "2014-09-11T19:59:18Z")

</div>

Serge,  
Thanks for the reply! The chef code retrieves some information form the chef server that I want to make available as vars (either host vars or group vars, or whatever) to the ansible run.

Yeah, I see what you mean about some of the vars in my code . . . what you’re seeing is a hacked up attempt to get something working . . . here it is again, a bit cleaner (prints are included to show flow)  
~/Code/mdansible⟫ cat inventory/vars\_plugins/chefdb.py

import chef

#print(“In chefdb.py”)

class VarsModule(object):

def **init** (self, inventory):

“”" constructor “”"

# Apparently nothing needed here

##print(“In **init** with %s” % dir(inventory) )  
pass

def run(self, host, vault\_password=None):  
#print("Starting run . .. ")  
api = chef.autoconfigure()  
#print(“In **init** , api = %s” % api )  
dbag, dbag\_item = ‘twelve\_factor\_environment’, ‘scratch-gmatz-sandbox-app-web’  
#print(“dbag = %s” % dbag)  
bag = chef.DataBag(dbag)  
#print bag  
item = bag[dbag\_item]  
return item.to\_dict()

if **name** == ‘ **main** ’:  
v = VarsModule(‘inv’)  
print v.run(‘hostname’)

Here it is failing when run by ansible:

⟫ ansible-playbook -i inventory site.yml  
Traceback (most recent call last):  
File “/opt/ansible/bin/ansible-playbook”, line 324, in   
sys.exit(main(sys.argv[1:]))  
File “/opt/ansible/bin/ansible-playbook”, line 160, in main  
inventory = ansible.inventory.Inventory(options.inventory, vault\_password=vault\_pass)  
File “/opt/ansible/lib/ansible/inventory/ **init**.py”, line 149, in **init**  
host.vars = utils.combine\_vars(host.vars, self.get\_host\_variables([host.name](http://host.name), vault\_password=self.\_vault\_password))  
File “/opt/ansible/lib/ansible/inventory/ **init**.py”, line 445, in get\_host\_variables  
self.\_vars\_per\_host[hostname] = self.\_get\_host\_variables(hostname, vault\_password=vault\_password)  
File “/opt/ansible/lib/ansible/inventory/ **init**.py”, line 457, in \_get\_host\_variables  
vars\_results = [plugin.run(host, vault\_password=vault\_password) for plugin in self.\_vars\_plugins if hasattr(plugin, ‘run’)]  
File “/home/gmatz/Code/mdansible/inventory/vars\_plugins/chefdb.py”, line 20, in run  
bag = chef.DataBag(dbag)  
File “/usr/local/lib/python2.7/dist-packages/chef/base.py”, line 58, in **init**  
data = self.api[self.url]  
TypeError: ‘NoneType’ object has no attribute ‘ **getitem** ’

---

<div class="post-metadata">

**Author:** ![Guy\_Matz1](https://avatars.discourse-cdn.com/v4/letter/g/b5e925/32.png) [@Guy\_Matz1](https://forum.ansible.com/u/Guy_Matz1)\
**Post date:** [September 11, 2014, 9:37pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/8 "2014-09-11T21:37:55Z")

</div>

Apologies!! Turns out to be a problem when I make my call to the chef DB and it has nothing to return . . . If I wrap the code that could fail in a try block it works:

def get\_host\_vars(self, host, vault\_password=None):  
print(“Getting vars for host %s” % host.name)  
data = {}  
try:  
api = chef.autoconfigure()  
dbag, dbag\_item = ‘twelve\_factor\_environment’, ‘scratch-gmatz-sandbox-app-web’  
bag = chef.DataBag(dbag)  
except Exception as e:  
return data  
#raise errors.AnsibleError(‘Unable to get chef data: %s’ % e.message)  
for k, v in bag[dbag\_item].raw\_data.items():  
data[k] = v  
return data

Two more question, though! ☹

1. Do I need to return the data in a certain format? I don’t seem to be able to access it . . . is there a good way to test this? I’m just dumping everything I can think of in a template
2. Does anyone know if I can restrict this var lookup to the hosts in my play? The chef query takes a few seconds

Thanks again,  
Guy

---

<div class="post-metadata">

**Author:** ![Serge\_van\_Ginderacht](https://avatars.discourse-cdn.com/v4/letter/s/b782af/32.png) [@Serge\_van\_Ginderacht](https://forum.ansible.com/u/Serge_van_Ginderacht)\
**Post date:** [September 12, 2014, 7:15am UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/9 "2014-09-12T07:15:36Z")

</div>

> 1. Do I need to return the data in a certain format? I don't seem to be  
> able to access it . . . is there a good way to test this? I'm just  
> dumping everything I can think of in a template

​Should be a dict holding all variables for the host/group called for.  
​

> 2. Does anyone know if I can restrict this var lookup to the hosts in my  
> play? The chef query takes a few seconds

​That is how it worked before 1.7, but the downside there was ​you had  
repeated calls for every host, re-parsing the same groups over and over.

Depending how the Chef code works, you could maybe do 1 call in the  
constructor to retrieve all data in 1 call, then return it per host and or  
group in the other functions.  
Or try to limit calls to get data for big groups only; or implement some  
caching.

I also have this utility that you can use to dump your data in a json,  
which you can then retrieve via an ansible inventory script:

[https://github.com/ginsys/ansible-plugins/blob/devel/bin/ansible-inventory](https://github.com/ginsys/ansible-plugins/blob/devel/bin/ansible-inventory)

&nbsp;&nbsp;Serge

---

<div class="post-metadata">

**Author:** ![Guy\_Matz1](https://avatars.discourse-cdn.com/v4/letter/g/b5e925/32.png) [@Guy\_Matz1](https://forum.ansible.com/u/Guy_Matz1)\
**Post date:** [September 12, 2014, 7:15pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/10 "2014-09-12T19:15:19Z")

</div>

Serge,  
Thank you for your effort here! So, do you have any idea how I can get at that data in a template?

Regards,  
Guy

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan1](https://avatars.discourse-cdn.com/v4/letter/m/5e9695/32.png) [@Michael\_DeHaan1](https://forum.ansible.com/u/Michael_DeHaan1)\
**Post date:** [September 12, 2014, 7:45pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/11 "2014-09-12T19:45:27Z")

</div>

A fact module to query your databag could be one thing, though it seems like you should just shoot it in the head and switch over to vault.

Your inventory script could also choose to provide this data perhaps, if it’s something dynamic.

---

<div class="post-metadata">

**Author:** ![Serge\_van\_Ginderacht](https://avatars.discourse-cdn.com/v4/letter/s/b782af/32.png) [@Serge\_van\_Ginderacht](https://forum.ansible.com/u/Serge_van_Ginderacht)\
**Post date:** [September 12, 2014, 9:25pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/12 "2014-09-12T21:25:22Z")

</div>

​  
That's what he's trying to do, and what we are troubleshooting 🙂

---

<div class="post-metadata">

**Author:** ![Serge\_van\_Ginderacht](https://avatars.discourse-cdn.com/v4/letter/s/b782af/32.png) [@Serge\_van\_Ginderacht](https://forum.ansible.com/u/Serge_van_Ginderacht)\
**Post date:** [September 12, 2014, 9:30pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/13 "2014-09-12T21:30:03Z")

</div>



---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan1](https://avatars.discourse-cdn.com/v4/letter/m/5e9695/32.png) [@Michael\_DeHaan1](https://forum.ansible.com/u/Michael_DeHaan1)\
**Post date:** [September 12, 2014, 9:53pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/14 "2014-09-12T21:53:35Z")

</div>

“That’s what he’s trying to do, and what we are troubleshooting :)”

I was replying to the discussion of lookup plugins, which I would suggest against.

I don’t feel vars\_plugins are ideal either.

---

<div class="post-metadata">

**Author:** ![Gabriel\_Burkholder](https://avatars.discourse-cdn.com/v4/letter/g/958977/32.png) [@Gabriel\_Burkholder](https://forum.ansible.com/u/Gabriel_Burkholder)\
**Post date:** [March 14, 2016, 10:25pm UTC](https://forum.ansible.com/t/developing-a-vars-plugin/16182/15 "2016-03-14T22:25:51Z")

</div>

Sorry to resurrect a zombie thread.

I recently wrote a simple lookup plugin to pull down chef databags, using pychef’s search functionality. I found this to be (at least in my case) simpler and more performant than accessing databags directly in pychef. In theory, it should also allow you to query a chef server for more than just databags, however, this doesn’t seem to work in my testing.

Thoughts? Comments? Criticisms?

`  
from ansible.errors import AnsibleError  
from ansible.plugins.lookup import LookupBase  
try:  
import chef  
except ImportError:  
AnsibleError(“Please pip install pychef to use this module”)

class LookupModule(LookupBase):

def run (self, terms, variables, \*\*kwargs):

api = chef.autoconfigure()

ret =   
for term in terms:

chef\_results =   
for result in chef.Search(term, \*\*kwargs):  
chef\_results.append(result[‘raw\_data’])

ret.append(chef\_results)

return ret  
`

Thank you!  
Gabriel Burkholder
