1

I have the following code:

if (!EAP-Message) {
    # Now check against the authorized_macs file
    <%- @filename.each do |key,value| -%>
    <%= key %>
    if (!ok) {
    <%- end -%>
                        eap
                    }
                    else {
                        # accept
                        update control {
                            Auth-Type := Accept
                        }
                    }
                }
                else {
                    # accept
                    update control {
                        Auth-Type := Accept
                    }   
                }
            }
            else {
                # accept
                update control {
                    Auth-Type := Accept
                }
            }
        }
        else {
            # accept
            update control {
                Auth-Type := Accept
            }
        }
    }
    else {
        # accept
        update control {
            Auth-Type := Accept
        }
    }
}
else {
    eap
}

I would like to indent the file properly by adding indentation or spaces or tabs after each loop, so that it looks like this:

if (!EAP-Message) {
    # Now check against the authorized_macs file
    authorized_macs_10
    if (!ok) {
        authorized_macs_11
        if (!ok) {
            authorized_macs_12
            if (!ok) {
                authorized_macs_13
                if (!ok) {
                    authorized_macs_14
                    if (!ok) {
                        eap
                    }
                    else {
                        # accept
                        update control {
                            Auth-Type := Accept
                        }
                    }
                }
                else {
                    # accept
                    update control {
                        Auth-Type := Accept
                    }
                }
            }
            else {
                # accept
                update control {
                    Auth-Type := Accept
                }
            }
        }
        else {
            # accept
            update control {
                Auth-Type := Accept
            }
        }
    }
    else {
        # accept
        update control {
            Auth-Type := Accept
        }
    }
}
else {
    eap
}

This is a part of the /etc/freeradius/sites-available/default of my freeradius puppet module, which is finally done. The @filename variable is a hiera_hash, which I get from a yaml file. Here is the relevant part:

test_freeradius::files:
'authorized_macs_10':
    macs: 
        - 'aaaaa'
        - 'bbbbb'
    vlan: '10' 
'authorized_macs_11':
    macs: 
        - 'ccccc' 
        - 'ddddd'  
    vlan: '11'
'authorized_macs_12':
    macs: 
        - 'eeeee'
        - 'fffff'
    vlan: '12'
'authorized_macs_13':
    macs:
        - 'ggggg'
        - 'hhhhh'
    vlan: '13'
'authorized_macs_14':
    macs:
        - 'iiiii'
        - 'jjjjj'
    vlan: '14'

And below is the define I use to create among other things the content of the default file:

define test_freeradius::files (

  $macs,
  $vlan,
  $macfile  = $title,

) {

  include test_freeradius::service

  ensure_resource('file', '/etc/freeradius/modules/files', {
      ensure  => 'file',
      owner   => 'root',
      group   => 'root',
      mode    => '0644',
      content => template("${module_name}/files.erb"),
      require => Class['test_freeradius::install'],
      notify  => Service['freeradius'],
    }
  )

  file { "/etc/freeradius/${macfile}" :
    ensure  => 'file',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template("${module_name}/macfiles.erb"),
    require => Class['test_freeradius::install'],
    notify  => Service['freeradius'],
  }

  ensure_resource('file', '/etc/freeradius/sites-available/default', {
      ensure  => 'file',
      owner   => 'root',
      group   => 'freerad',
      mode    => '0644',
      content => template("${module_name}/default.erb"),
      require => Class['test_freeradius::install'],
      notify  => Service['freeradius'],
    }
  )

  ensure_resource('file', '/etc/freeradius/sites-enabled/default', {
      ensure => 'link',
      target => '/etc/freeradius/sites-available/default',
    }
  )
}

The loop does what it should, but without indentation, I tried different things like adding <%= " "%> at several positions and creating a variable for the if (!ok) { part, unfortunately without success, maybe I should restructure the code? any ideas?

Max
  • 543
  • 6
  • 17
  • Can't you just dump `@filename` into a JavaScript variable (probably using JSON) and then iterate over the data in JavaScript? That approach tends to be a lot cleaner and easier to understand than writing a code generator. – mu is too short May 16 '17 at 17:27
  • 1
    @muistooshort, that might be easier, but it's irrelevant. The point here is to build a file that will be deployed on the Puppet client. The required nature of the file is a characteristic of the package that is going to use it. One is rarely at liberty to demand that the package work differently or accept a different form of input than it already does. – John Bollinger May 16 '17 at 17:35
  • @muistooshort, thanks for the reply! Well I never thought about that, since I don't code in JavaScript, only Bash and Python, a and bit Ruby and Perl. Our hiera backend in the company is yaml. So I have to stick to this. But I am interesting how this would be in JavaScript! – Max May 16 '17 at 17:42
  • I misread your question and thought you were producing JavaScript rather than some Puppet-specific configuration. – mu is too short May 16 '17 at 17:56

2 Answers2

3

You ought to be able to generate appropriate indentation via Ruby scriptlet code by building up an indentation string as you iterate. For example,

if (!EAP-Message) {
    # Now check against the authorized_macs file
    <%- indent = '' -%>
    <%- @filename.each do |key,value| -%>
    <%= indent %><%= key %>
    <%= indent %>if (!ok) {
    <%- indent += '    ' -%>
    <%- end -%>
                        eap
    ...

It seems to me, though, that you might have another problem. Can you be confident that the @filenames hash will always contain exactly five entries? If it contains some other number of entries then your template will produce malformed output, because there will be either too many or too few block closures.

You could address that by using an approach similar to the one I already described to build up the block closures in a string as you iterate, and then outputting the string at the appropriate point (instead of using template text to close the blocks).

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I have this point in my mind, since the hash could have more or less than five entries depending on the location of the company and how the network infrastructure looks like. I wanted to resolve the indentation issue and then use the same approach to generate the block closures. Will try it and post the final result. You seem to be my savior John. Thanks man! – Max May 16 '17 at 17:50
  • Works perfect! I'll post the final code when I am done with block closures! Thanks again John. – Max May 16 '17 at 17:58
1

this is the final result of the freeradius module. These are the relevant code fragments I've been asking for help.

default.erb

if (!EAP-Message) {
    # Now check against the authorized_macs file
    <%- indent = '' -%>
    <%- @filename.each do |key,value| -%>
    <%= indent %><%= key %>
    <%= indent %>if (!ok) {
    <%- indent += '    ' -%>
    <%- end -%>
    <%= indent %>eap
<%- @filename.each do |key,value| -%>
    <%= indent %>}
    <%= indent %>else {
        <%= indent %># accept
        <%= indent %>update control {
            <%= indent %>Auth-Type := Accept
        <%= indent %>}
    <%= indent %>}
<% indent = indent.slice(0..-5) -%>
<%- end -%> 
}
else {
    eap
}

The post-auth section of the virtual server configuration was a bit tricky, as I had to read the hash, write down the keys and assign the vlans in reverse order, so I converted the hash to an array, revered it and converted the array back to hash.

inner-tunnel

post-auth {
    <%- indent = '' -%>
    <%- @filename.each do |key,value| -%>
    <%= indent %><%= key %>
    <%= indent %>if (!ok) {
    <%- indent += '    ' -%>
    <%- end -%>
    <%= indent %>ldap
<%- @new_files = Hash[@filename.to_a.reverse].to_hash -%>
<%- @new_files.each do |key,value| -%>
<%= indent %>}
<%= indent %>else {
    <%= indent %>update reply {
        <%= indent %>Tunnel-Type = VLAN
        <%= indent %>Tunnel-Medium-Type = IEEE-802
        <%= indent %>Tunnel-Private-Group-ID =  <%= value['vlan'] %>
    <%= indent %>}
<%= indent %>}
<%- indent = indent.slice(0..-5) -%>
<%- end -%>

    ldap
<%- @groups.each do |key,value| -%>
<%- if key == 'vlan_10' -%>
    if (LDAP-Group == vlan_10) {
        update reply {
            Tunnel-Type = VLAN
            Tunnel-Medium-Type = IEEE-802
            Tunnel-Private-Group-ID = 10
        }
    }
<%- else -%>
    elsif (LDAP-Group == <%= key %>) {
        update reply {
            Tunnel-Type = VLAN
            Tunnel-Medium-Type = IEEE-802
            Tunnel-Private-Group-ID = <%= value['vlan'] %>
        }
    }
<%- end -%>
<%- end -%>

    Post-Auth-Type REJECT {
        attr_filter.access_reject
    }
}

Thank you all, especially John!

Max
  • 543
  • 6
  • 17