Ansible Custom Module: Are print statements possible? - ansible

I have an Ansible Custom Module for performing a specific task in my playbook. I want to debug specific variables inside this module.
Is there a way we can print anything inside this custom module? In the example below, print "Hello".
Please check the following snippet from the Custom Module. I am passing a jobid as an argument to this module.
class dcsjob():
def __init__(self, arglist):
self.jobid = self.arglist[0]
def checkandwaitforjob(self):
print("Hello")
def run_module():
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
dcsjobobj = dcsjob([module.params['jobid']])
output = dcsjobobj.checkandwaitforjob()

Given an example module from Developing modules- Creating a module with modification
library/print.py
#!/usr/bin/python
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.module_utils.basic import AnsibleModule
def run_module():
module_args = dict(
jobid=dict(type='str', required=True),
verbose=dict(type='bool', required=False, default=False)
)
result = dict(
changed=False,
jobid=''
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=False
)
result['jobid'] = module.params['jobid']
if module.params['verbose']:
result['print'] = 'Hello'
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
and called from an example playbook print.yml
---
- hosts: localhost
become: false
gather_facts: false
vars:
ID: 123
tasks:
- name: Run non-verbose
print:
jobid: "{{ ID }}"
verbose: false
register: result
- name: Show result
debug:
msg: "{{ result }}"
- name: Run verbose
print:
jobid: "{{ ID }}"
verbose: true
register: result
- name: Show verbose result
debug:
msg: "{{ result }}"
it will result into an output of
TASK [Run non-verbose] **********
ok: [localhost]
TASK [Show result] **************
ok: [localhost] =>
msg:
changed: false
failed: false
jobid: '123'
TASK [Run verbose] **************
ok: [localhost]
TASK [Show verbose result] ******
ok: [localhost] =>
msg:
changed: false
failed: false
jobid: '123'
print: Hello
printing the jobid and Hello when in verbose mode.
This could also be utilized with check_mode
module_args = dict(
jobid=dict(type='str', required=True)
)
result = dict(
changed=False,
check_mode=False,
jobid=''
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
result['jobid'] = module.params['jobid']
if module.check_mode:
result['check_mode'] = True
result['print'] = 'Hello'
which would change the behavior if --check is applied or not.
Further Documentation
Developing modules- Creating a module
Debugging modules - Simple debugging
Since print() statements do not work inside modules, raising an exception is a good approach if you just want to see some specific data.

Related

How do I extract these values from a Ruby array

I'm debugging some Ruby code that uses the AWS SDK to extract EC2 tags:
resource = Aws::EC2::Resource.new(client: client)
resource.instances({filters: fetch(:ec2_filters)}).each do |instance|
puts "DEBUG: #{instance.tags}"
app_tag = instance.tags.select { |t| t.key == 'application' }.pop.value
We recently updated the jmespath dependency to pick up this change.
BEFORE updating:
DEBUG: [#<struct Aws::EC2::Types::Tag key="application", value="api">, #<struct Aws::EC2::Types::Tag key="environment", value="production">]
AFTER updating jmespath
DEBUG: [{:key=>"application", :value=>"api"}, {:key=>"environment", :value=>"production"}]
The .select { |t| t.key == 'application' }. now throws an error:
ArgumentError: wrong number of arguments (given 0, expected 1)
Can anyone advise how to parse the new response format? (i.e. [{:key=>"application", :value=>"api"}...)
UPDATE 1
# This finds the correct item, but I'm not sure how to extract ':value'
puts "DEBUG: #{instance.tags.select { |t| t[:key] == 'application' }}"
DEBUG: [{:key=>"application", :value=>"api"}]
UPDATE 2
This works:
puts "DEBUG: #{instance.tags.select { |t| t[:key] == 'application' }[0][:value]}"
DEBUG: api
Before the change the value of instance.tags used to be a struct, where you can access the key attribute by using dot notation, now you have a hash so you can use :[] instead, e.g:
Struct.new(:key).new('hej').key # "hej"
({ key: 'hej' })[:key] # "hej"

Passing filters(functions) to custom ansible filter plugin

I'd like to pass functions(such as contains, defined, etc) to my ansible-filter plugin. In a same manner that is passed to map() for example.
e.g:
# playbook
- debug:
msg: "{{ my_dict | my_filter('contains', 'somevalue') }}"
# plugin
class FilterModule(object):
def filters(self):
return {'my_filter': self.my_filter}
def my_filter(data, function):
something = function(data)
return something
However, In official documentation and across the internet I only found examples of how to pass data but not callables.

Update hash values with hash key

I'm facing a problem that I couldn't find a working solution yet.
I have my YAML config file for the environment, let's call it development.yml.
This file is used to create the hash that should be updated:
data = YAML.load_file(File.join(Rails.root, 'config', 'environments', 'development.yml'))
What I'm trying to accomplish is something along these lines. Let's suppose we have an element of the sort
data['server']['test']['user']
data['server']['test']['password']
What I want to have is:
data['server']['test']['user'] = #{Server.Test.User}
data['server']['test']['password'] = #{Server.Test.Password}
The idea is to create a placeholder for each value that is the key mapping for that value dynamically, going until the last level of the hash and replacing the value with the mapping to this value, concatenating the keys.
Sorry, it doesn't solve my problem. The location data['server']['test']['user'] will be built dynamically, via a loop that will go through a nested Hash. The only way I found to do it was to append to the string the key for the current iteration of the Hash. At the end, I have a string like "data['server']['test']['name']", which I was thinking on converting to a variable data['server']['test']['name'] and then assigning to this variable the value #{Server.Test.Name}. Reading my question I'm not sure if this is clear, I hope this helps to clarify it.
Input sample:
api: 'active'
server:
test:
user: 'test'
password: 'passwordfortest'
prod:
user: 'nottest'
password: 'morecomplicatedthantest'
In this case, the final result should be to update this yml file in this way:
api: #{Api}
server:
test:
user: #{Server.Test.User}
password: #{Server.Test.Password}
prod:
user: #{Server.Prod.User}
password: #{Server.Prod.Password}
It sounds silly, but I couldn't figure out a way to do it.
I am posting another answer now since I realize what the question is all about.
Use Iteraptor gem:
require 'iteraptor'
require 'yaml'
# or load from file
yaml = <<-YAML.squish
api: 'active'
server:
test:
user: 'test'
password: 'passwordfortest'
prod:
user: 'nottest'
password: 'morecomplicatedthantest'
YAML
mapped =
yaml.iteraptor.map(full_parent: true) do |parent, (k, _)|
v = parent.map(&:capitalize).join('.')
[k, "\#{#{v}}"]
end
puts YAML.dump(mapped)
#⇒ ---
# api: "#{Api}"
# server:
# test:
# user: "#{Server.Test.User}"
# password: "#{Server.Test.Password}"
# prod:
# user: "#{Server.Prod.User}"
# password: "#{Server.Prod.Password}"
puts YAML.dump(mapped).delete('"')
#⇒ ---
# api: #{Api}
# server:
# test:
# user: #{Server.Test.User}
# password: #{Server.Test.Password}
# prod:
# user: #{Server.Prod.User}
# password: #{Server.Prod.Password}
Use String#%:
input = %|
data['server']['host']['name'] = %{server_host}
data['server']['host']['user'] = %{server_host_user}
data['server']['host']['password'] = %{server_host_password}
|
puts (
input % {server_host: "Foo",
server_host_user: "Bar",
server_host_password: "Baz"})
#⇒ data['server']['host']['name'] = Foo
# data['server']['host']['user'] = Bar
# data['server']['host']['password'] = Baz
You can not add key-value pair to a string.
data['server']['host'] # => which results in a string
Option 1:
You can either save Server.Host as host name in the hash
data['server']['host']['name'] = "#{Server.Host}"
data['server']['host']['user'] = "#{Server.Host.User}"
data['server']['host']['password'] = "#{Server.Host.Password}"
Option 2:
You can construct the hash in a single step with Host as key.
data['server']['host'] = { "#{Server.Host}" => {
'user' => "#{Server.Host.User}",
'password' => "#{Server.Host.Password}"
}
}

Print %YAML <version> on output`s header

This is the code:
file '/etc/myproduct/myfile.yaml' do
content node['myproduct']['config'].to_hash.to_yaml( :UseVersion => true, :UseHeader => true )
mode 0644
owner 'root'
group 'root'
end
output:
---
vars:
address-groups:
Im trying to add %YAML 1.1 above --- so it will look like this:
%YAML 1.1
---
vars:
address-groups:
also tried to_yaml only - no good.
I would probably just do it the brute-force way:
content "%YAML 1.1\n" + node['myproduct']['config'].to_hash.to_yaml
I'm sure there is a better way, but that works so ... :)

Why does blockinfile with state=absent not work for these lines?

I have a file with the following block of text in it. I have text before and after the text block
other_user:
hash: JKJ;LKJA;LDKJF;LKJA;LKJIUR;JFKLJDQPIRQKJ;LKFJPOQJ
#password is: some_pw0
logstash:
hash: $fj;kdjjfajf;ajKFJ;dfj;dkfja;dfjFJ:LFJj;kj;lfkajs
#password is: some_pw
other_user1:
hash: JJKLJDRKJOPIQMVOIUROIJFAUROJJFIUQWKERJJFKQURJAKDJ
#password is: some_pw1
I'm trying to remove the block for the logstash user using this code, but it does NOT remove it.
- name: Delete existing logstash user
blockinfile:
dest: /path_to_file/foo.yml
state: absent
block: |
logstash:
hash: $fj;kdjjfajf;ajKFJ;dfj;dkfja;dfjFJ:LFJj;kj;lfkajs
#password is: some_pw
I expect the result to be:
other_user:
hash: JKJ;LKJA;LDKJF;LKJA;LKJIUR;JFKLJDQPIRQKJ;LKFJPOQJ
#password is: some_pw0
other_user1:
hash: JJKLJDRKJOPIQMVOIUROIJFAUROJJFIUQWKERJJFKQURJAKDJ
#password is: some_pw1
What am I missing?
There's a thing about blockinfile. Pay close attention at the description:
This module will insert/update/remove a block of multi-line text surrounded by customizable marker lines.
The default value for markers: # {mark} ANSIBLE MANAGED BLOCK where mark is BEGIN/END.
So if there are no markers in the file, module will treat it as no block found.

Resources