slal
slal

Reputation: 23

Generating dynamic YAML file from a for loop in Python

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 .txtfile 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 .txtfile and also first hostname variable in template.yml is not taken into account.

Upvotes: 0

Views: 4990

Answers (3)

Icebreaker454
Icebreaker454

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

Krishna Chaurasia
Krishna Chaurasia

Reputation: 9552

There are some problems in the current code:

  1. With return config, you are just exiting the function without writing to the file.
  2. You open the file in write mode w every time so the contents get overwritten.
  3. You are adding the 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

flyx
flyx

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

Related Questions