Reputation: 23
I have a template.yml
with below contents:
hostname:
image: my_image:latest
script:
- ansible -m command -a "echo Hi" hostname
Using python, I need to read a .txt
file with below contents:
server1.example.com
server2.example.com
server3.example.com
...
and using some loop and replace the hostname variable in both places in above template.yml
. The all configs need to be in a single output.yml
file.
I have been trying to use python yaml library and its yaml.update and yaml.dump features but had no luck in storing all the generated configs in the same output file. Any help with this is appreciated.
Updates:
I have added the code I tried below:
import yaml
def generate_yml(host):
with open('template.yml') as f:
config = yaml.safe_load(f)
config['hostname']['script'] = "ansible -m command -a 'echo Hi' " + host
return config
with open('output.yml', 'w') as f:
yaml.dump(config, f, default_flow_style=False)
hostfile = open("test.txt", "r")
for host in hostfile:
host = host.rstrip("\n")
generate_yml(host)
myfile.close()
and for yaml_update
, I tried:
with open('output.yml','r') as f:
config = yaml.load(yamlfile)
config['hostname']['script'] = "ansible -m command -a 'echo Hi' " + host
config['hostname'].update(config)
with open('output.yml', 'w') as yamlfile:
yaml.dump(config, yamlfile, default_flow_style=False)
Both approaches write the configs for only last entry in the .txt
file and also first hostname variable in template.yml
is not taken into account.
Upvotes: 0
Views: 4990
Reputation: 1071
Try this.
It loads the template as a dictionary, and forms a new dictionary with proper hostname
substitution for each host, and dumps the whole thing to the output.yml
file. Note that this requires PyYaml - pip insatll pyyaml
import yaml
with open("./template.yml") as f:
template = yaml.load(f)
HOSTS = [
"host_a",
"host_b",
"host_c"
]
output = {host: template["hostname"] for host in HOSTS}
with open("./output.yml", "w") as f:
yaml.dump(output, f)
Upvotes: 0
Reputation: 9552
There are some problems in the current code:
return config
, you are just exiting the function without writing to the file.w
every time so the contents get overwritten.script
part as a string object but it should be a list
for the right yaml
.The correct implementation should be:
import yaml
def generate_yml(host):
with open('template.yml') as f:
config = yaml.safe_load(f)
config[host] = config['hostname'].copy()
config[host]['script'] = ["ansible -m command -a 'echo Hi' " + host] # add the command as a list for the correct yaml
del config['hostname'] # del the 'hostname' key from config
with open('output.yml', 'a') as f: # open the file in append mode
yaml.dump(config, f, default_flow_style=False)
hostfile = open("test.txt", "r")
for host in hostfile:
host = host.rstrip("\n")
generate_yml(host)
hostfile.close()
Output:
server1.example.com:
image: my_image:latest
script:
- ansible -m command -a 'echo Hi' server1.example.com
server2.example.com:
image: my_image:latest
script:
- ansible -m command -a 'echo Hi' server2.example.com
server3.example.com:
image: my_image:latest
script:
- ansible -m command -a 'echo Hi' server3.example.com
Upvotes: 2
Reputation: 39638
YAML is not a templating engine. Have you considered using a templating engine?
This is how you could do it with pystache:
import pystache, yaml
input = """
{{#hostnames}}
{{.}}:
image: my_image:latest
script:
- ansible -m command -a "echo Hi" {{.}}
{{/hostnames}}
"""
vars = """
server1.example.com
server2.example.com
server3.example.com
"""
print(pystache.render(input, {"hostnames": [h for h in vars.splitlines() if h]}))
Output:
server1.example.com:
image: my_image:latest
script:
- ansible -m command -a "echo Hi" server1.example.com
server2.example.com:
image: my_image:latest
script:
- ansible -m command -a "echo Hi" server2.example.com
server3.example.com:
image: my_image:latest
script:
- ansible -m command -a "echo Hi" server3.example.com
Compared to actually parsing the input as YAML, you can keep formatting (e.g. comments and original indentation) when using a templating engine.
Upvotes: 1