"Listing" the keys in a hash as an array

8,850 views
Skip to first unread message

Miki Shapiro

unread,
Apr 19, 2011, 5:42:10 AM4/19/11
to puppet...@googlegroups.com
Hi all

I've got node definition on the site with server interface, bonding and TCP/IP configurations inscribed in a per-node [ hash + accompanying array-of-interface-names ]
(e.g. $array = [ 'eth0', 'eth1' ]
$hash = {
   eth0 => {
      ipaddress => 1.2.3.4,
      ... }
   eth1 => {
      ipaddress => 5.6.7.8,
      ... }
}

What I have right now works, but is clunky and could probably be written better.

My 2 questions are:
1. Can I somehow get a list of keys from the hash itself (a list that will behave like an array that I can feed into a defined type) instead of "manually" defining $array for this purpose?

2. If you look further down inside in this post, the code (taken from inside the base class) does this:
[a] gets the hash with the configuration passed in as a global variable
[b] copies the hash elements into local variables (because a local variable can be double-quoted around, and behave nicely even if it wasn't set and is blank, whereas a hash element cannot)
[c] invokes the interface-configuration defined type with the local variables as parameters.
A very messy roundabout way of doing things. Can anyone suggest an easier way (tho still ultimately has to call base::network-common::interface)

In the node file I have a configuration block that looks like this:
        $netiflist      = [ 'eth0' ]
        $netifcfg       = {
                eth0 => {      ipaddress => "1.2.3.4",
                                netmask => "255.255.255.0",
                                gateway => "1.2.3.1",
                                defaultgateway => "yes" },
        }
        include base

Then in a subclass of base I have:

class base-subclass {
# Iterate for all the interfaces that need to be configured:
if $netiflist           { base::network-common::dointerfaces {[$netiflist]:}}
}

# the actual defined type:
define base::network-common::dointerfaces() {
      # copy hash elements into local variables:
        $myipaddress       = $netifcfg[$name][ipaddress]
        $mynetmask         = $netifcfg[$name][netmask]
        $mygateway         = $netifcfg[$name][gateway]
        $mydefaultgateway  = $netifcfg[$name][defaultgateway]
        $myhwaddress       = $netifcfg[$name][hwaddress]
        $myroutes          = $netifcfg[$name][routes]
        $myensure          = $netifcfg[$name][ensure]

# and finally use the double-quoted local variables to call the interface defined type:
        base::network-common::interface { $name:
                ipaddress       =>      "$myipaddress",
                netmask         =>      "$mynetmask",
                gateway         =>      "$mygateway",
                defaultgateway  =>      "$mydefaultgateway",
                hwaddress       =>      "$myhwaddress",
                routes          =>      "$myroutes",
                ensure          =>      "$myensure"
        }
}
...



Ohad Levy

unread,
Apr 19, 2011, 7:09:29 AM4/19/11
to puppet...@googlegroups.com
On Tue, Apr 19, 2011 at 8:42 AM, Miki Shapiro <mikis...@gmail.com> wrote:
 clunky and could probably be written better.

My 2 questions are:
1. Can I somehow get a list of keys from the hash itself (a list that will behave like an array that I can feed into a defined type) instead of "manually" defining $array for this purpose?

fuzzy workaround:

$keys = inline_template("<%= hash.keys %>")
 

Felix Frank

unread,
Apr 19, 2011, 9:55:19 AM4/19/11
to puppet...@googlegroups.com

Ah, but won't that imply a "to_s" on the resulting array of keys?

You may want to join the keys (say using ",") and split by that same
comma in the DSL. Pseudocode:

$keys = split(",", inline_template("<%= hash.keys.join(',') %>"))

Cheers,
Felix

Message has been deleted

Miki Shapiro

unread,
Apr 20, 2011, 1:28:25 AM4/20/11
to puppet...@googlegroups.com
I must be doing something wrong...
As suggested by Ohad:
Manifest says:
$keys = inline_template("<%= netifcfg.keys %>")
exec { "/bin/echo keys are $keys and netifcfg is $netifcfg": logoutput => true }

Output says:
notice: /Stage[main]/Base::Network-common/Exec[/bin/echo keys are bond0bond1 and netifcfg is bond0ipaddress10.15.69.177netmask255.255.254.0defaultgatewayyesgateway10.15.68.1bond1ipadderss1.2.3.4netmask255.255.254.0gateway1.2.3.1]/returns: is notrun, should be 0 (noop)
(DOES work with 1 element tho)
As suggested by Felix:
Manifest says:
        $keys = split(",", inline_template("<%= netifcfg.keys.join(',') %>"))
        exec { "/bin/echo keys are $keys and netifcfg is $netifcfg": logoutput => true }
Output says:
notice: /Stage[main]/Base::Network-common/Exec[/bin/echo keys are , and netifcfg is bond0ipaddress10.15.69.177netmask255.255.254.0defaultgatewayyesgateway10.15.68.1bond1ipadderss1.2.3.4netmask255.255.254.0gateway1.2.3.1]/returns: keys are , and netifcfg is bond0ipaddress10.15.69.177netmask255.255.254.0defaultgatewayyesgateway10.15.68.1bond1ipadderss1.2.3.4netmask255.255.254.0gateway1.2.3.1
(Split doesn't seem to work).

Nigel Kersten

unread,
Apr 20, 2011, 3:08:52 AM4/20/11
to puppet...@googlegroups.com
On Tue, Apr 19, 2011 at 6:28 PM, Miki Shapiro <mikis...@gmail.com> wrote:

> $keys = split(",", inline_template("<%= netifcfg.keys.join(',') %>"))

Honestly, I'd put this together as a Puppet function at this point, as
that's just resulting in something rather unmaintainable.

http://docs.puppetlabs.com/guides/custom_functions.html

I'll have a quick stab at it in a second.

Nigel Kersten

unread,
Apr 20, 2011, 4:14:08 AM4/20/11
to puppet...@googlegroups.com

module Puppet::Parser::Functions
newfunction(:hash_keys, :type => :rvalue) do |args|
unless args[0].is_a?(Hash)
Puppet.warning "hash_keys takes one argument, the input hash"
nil
else
args[0].keys
end
end
end


save to lib/puppet/parser/functions/ in a module.


--
Nigel Kersten
Product, Puppet Labs
@nigelkersten

Miki Shapiro

unread,
Apr 20, 2011, 4:46:28 AM4/20/11
to puppet...@googlegroups.com
Thanks guys

I ended up breaking up what Felix&Ohad suggested into two lines to make it work:
        $keycsv= inline_template("<%= netifcfg.keys.join(',') %>")
        $keys = split ($keycsv,',')
Yep, it's a 'hack' (sorry, Ohad, workaround ;)), but it saves me having to add puppet functions into SVN...
(which is quite involved in my case because we support a big site with versioned modules and a custom module-versioning mechanism that at present does not cover custom functions (a long, painful topic in its own right) so I need to contain this code in the module using it - making this workaround perfect.

Should we perhaps nudge the above function into mainstream puppet? Being able to list the keys in a hash is quite a crucial capability to have.

Ohad Levy

unread,
Apr 20, 2011, 5:54:44 AM4/20/11
to puppet...@googlegroups.com
On Wed, Apr 20, 2011 at 7:46 AM, Miki Shapiro <mikis...@gmail.com> wrote:
Thanks guys

I ended up breaking up what Felix&Ohad suggested into two lines to make it work:
        $keycsv= inline_template("<%= netifcfg.keys.join(',') %>")
        $keys = split ($keycsv,',')
Yep, it's a 'hack' (sorry, Ohad, workaround ;)), but it saves me having to add puppet functions into SVN...
(which is quite involved in my case because we support a big site with versioned modules and a custom module-versioning mechanism that at present does not cover custom functions (a long, painful topic in its own right) so I need to contain this code in the module using it - making this workaround perfect.

when having this issue, one can use a plain template not to clutter the manifest, not sure if it apply 100%  in this case, but for most things a template == function... for example

$var = template ("x.erb")

x.erb-->

<%=
netifcfg.keys.collect |key|
  key.reverse!  
  key.upcase!
  key += "woot"
  File.open("/tmp/#{key}"
end
%>

Ohad

Should we perhaps nudge the above function into mainstream puppet? Being able to list the keys in a hash is quite a crucial capability to have.

--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
To post to this group, send email to puppet...@googlegroups.com.
To unsubscribe from this group, send email to puppet-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-users?hl=en.

Felix Frank

unread,
Apr 20, 2011, 7:17:15 AM4/20/11
to puppet...@googlegroups.com
> module Puppet::Parser::Functions
> newfunction(:hash_keys, :type => :rvalue) do |args|
> unless args[0].is_a?(Hash)
> Puppet.warning "hash_keys takes one argument, the input hash"
> nil
> else
> args[0].keys
> end
> end
> end
>
>
> save to lib/puppet/parser/functions/ in a module.

I think this should be mainline *hint, hint* ;-)

Cheers,
Felix

Thomas Bellman

unread,
Apr 20, 2011, 11:16:52 AM4/20/11
to puppet...@googlegroups.com
On 2011-04-20 03:28, Miki Shapiro wrote:

> As suggested by Felix:
> Manifest says:
> $keys = split(",", inline_template("<%= netifcfg.keys.join(',')
> %>"))
> exec { "/bin/echo keys are $keys and netifcfg is $netifcfg":
> logoutput => true }
>
> Output says:
> notice: /Stage[main]/Base::Network-common/Exec[/bin/echo keys are , and netifcfg is bond0ipaddress10.15.69.177netmask255.255.254.0defaultgatewayyesgateway10.15.68.1bond1ipadderss1.2.3.4netmask255.255.254.0gateway1.2.3.1]/returns: keys are , and netifcfg is bond0ipaddress10.15.69.177netmask255.255.254.0defaultgatewayyesgateway10.15.68.1bond1ipadderss1.2.3.4netmask255.255.254.0gateway1.2.3.1
> (Split doesn't seem to work).

You have two errors. The first is that the split() function takes its
parameters in the other order from what Felix wrote. The string to
split on goes second, so you should do:

$keys = split(inline_template("<%= netifcfg.keys.join(',') %>"), ",")

The second error is that when you expand $keys within a string literal,
the elements of the array will be joined with nothing inbetween. Try
this instead:

notify {
$keys: ;
}

Or perhaps:

notify {
netifcfg-keys:
message => shellquote("Keys", "are:", $keys);
}

Those will show you better the real value of $keys.


/Bellman

Nigel Kersten

unread,
Apr 20, 2011, 3:23:31 PM4/20/11
to puppet...@googlegroups.com

I might take this as the first step towards collecting a bunch of
these in a module, and if there's enough interest in it during the
life of 2.7.x, we'll merge some of them into Telly, the next feature
release.

Reply all
Reply to author
Forward
0 new messages