are there iterators and loops in puppet? - deployment

When I define(?) a resource e.g. to ensure dir structure, are there any loops available?
Like that:
for X in [app1,app2] do:
file { '/opt/app/' + X:
ensure => directory,
owner => 'root',
group => 'root',
mode => '0644',
}
I have tens of directories and I am really tired with declaring it in puppet.. It would take 15 LOC of bash.
Any ideas?

Older versions of the puppet language have no support for loops.
But you can use an array instead of a simple string for the title and declare several resources at the same time with the same params:
$b = '/opt/app'
file { [ "$b/app1", "$b/app2" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0644,
}
You can also declare many resources of the same type with different params by ending each resource with a ;, which is a bit more compact than repeating the file and the {s and }s:
file {
[ "$b/app1", "$b/app2" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0755;
[ "$b/app1/secret", "$b/app2/secret" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0700;
}
In the specific case of files, you can set up a source and use recursion:
file { "/opt/app":
source => "puppet:///appsmodule/appsdir",
recurse => true;
}
(that would require having a source of that directory structure for puppet to use as the source)
You can define a new resource type to reuse a portion of the param multiple times:
define foo {
file {
"/tmp/app/${title}":
ensure => directory,
owner => 'root',
mode => 0755;
"/tmp/otherapp/${title}":
ensure => link,
target => "/tmp/app/${title}",
require => File["/tmp/app/${title}"]
}
}
foo { ["app1", "app2", "app3", "app4"]: }
Starting with Puppet 2.6, there's a Ruby DSL available that has all the looping functionality you could ask for: http://www.puppetlabs.com/blog/ruby-dsl/ (I've never used it, however). In Puppet 3.2, they introduced some experimental loops, however those features may change or go away in later releases.

As of version 3.2 there are lambdas
You must set parser = future in puppet.conf.
$a = [1,2,3]
each($a) |$value| { notice $value }
Another option for declaring multiple defined types is create_resources. Pass it a hash of hashes:
create_resources(file, {
'/tmp/test1' => {
ensure => directory,
owner => 'root',
group => 'root',
mode => '0644',
},
'/tmp/test2' => {
ensure => directory,
owner => 'www-data',
group => 'www-data',
mode => '0755',
},
})

As of Puppet 4 (and the "future parser" of late versions of Puppet 3) the Puppet DSL has iteration functions similar in form and function to some of the methods of Ruby arrays and hashes:
each - evaluates a block of code (formally, a lambda) for each element of an array or hash
filter - applies a lambda to each element of an array or hash and returns an array or hash of those for which the lambda evaluated to true
map - applies a lambda to each element of an array or hash, and returns an array of the results
reduce - applies a lambda to each element of an array or hash to build up a single result, which it returns
There is no indexed for loop along the lines of C's or Java's, but you can combine array sectioning with any of the functions above to achieve iteration over a subset of a data structure. There is no indefinite iteration along the lines of a C or Java while loop.
Of course, you can still use the resource-centric approaches described in other answers, and sometimes one of those is indeed the best available approach. You cannot any longer use Ruby DSL, however; it is removed altogether from Puppet 4. Among the iteration functions, the ability to define custom functions, the ascension of data-centric approaches into favor, and all Puppet's historic standard features, Ruby DSL seems not much missed.

Yes. "Ruby DSL" could help, just use file extension ".rb" instead of ".pp" in manifest and you can define puppet "type" like this:
define 'myapps::structure', :applist do
#applist.each do |app|
file( #name+'/'+app,
:ensure => directory,
:owner => 'root',
:group => 'root',
:mode => '0644')
end
end
Classes and nodes also can be defined in similar way. Notice however that this feature is deprecated since release 3

Related

Puppet - deep merge lookup default_value with hiera hash returned

I am trying to deep merge the lookup 'default_value' or 'default_values_hash' with the hash returned from a lookup. It will not merge and the default_value only appear to take effect if the hiera title isn't found at all. I cannot set resource defaults here as the values returned are later processed and not actual resource keys yet.
I've tried numerous variations including 'default_value', 'default_values_hash'. I'm seeking a way to just set a default hash in the manifest and have it deep merge with hiera to create a larger hash.
Manifest:
class test (
Hash $result = lookup('test::my_hash', {merge => 'deep', default_values_hash => {foo => 'bar', this => 'that', him => 'her'}}),
){
notice($result)
}
include test
Hiera:
---
test::my_hash:
foo: 'nobar'
this: 'then'
desired result (deep merge):
{ foo => 'nobar', this => 'then', him => 'her' }
actual result (returns hiera hash only):
{ foo => 'nobar', this => 'then' }
UPDATE:
I got it working with the code below. Still interested if anyone has a better solution.
class test (
$stuff = {
foo => 'bar',
this => 'that',
him => 'her'
},
Hash $result = deep_merge($stuff, lookup('test::my_hash')),
){
notice($result)
}
Unfortunately, that is the way lookup works. The default value is only used if no other value is found. The documentation for the default in lookup says
If present, lookup returns this when it can’t find a normal value. Default values are never merged with found values.
Your version using the deep_merge function from stdlib appears to be the best solution.
class foo {
$default_foo_attribute = {
foo => 'bar',
this => 'that',
him => 'her',
}
$attribute = deep_merge($default_foo_attribute,
lookup('foo::attribute',
Hash[String, String],
'deep',
{})
notice($attribute)
}

Get Values out of Complex Perl Hash Structures

With the following Code I can fetch Data stored in a Hash from a Database and print it out:
use Data::Dumper;
my $fdx = $s->field(); # get Hashreference from Database
print Dumper($fdx); # print out Hash
The (important part of the) Output looks like this:
$VAR1 = bless( {
'selectable' => 'true',
'_PARENT_OBJECT' => bless( {
'dirtyFlag' => 1,
'path' => undef,
'testItems' => [],
'data' => {
'TEST_10' => {
'atm_rundatahistory' => {
'1523964918' => {
'atm_prid' => {
'content' => '',
'label' => 'Problem Report IDs',
'raw' => ''
}, ...
'1523964410' => {
'atm_prid' => {
'label' => 'Problem Report IDs',
'raw' => '23361234',
'content' => '23361234'
}, ...
'Test_10' is one of hundreds of Tests, '1523964918' is one of many unix timestamps, so basically its a 32 Bit Integer, but I dont know which numbers the timestamps contain.
How do I print out / access the values for 'content' (in this case '23361234') of the most inner Hashes, for all Tests and unix timestamps, if they exist?
from here on I will describe my thoughts and what I have tried, its not necessary to read any further for this question.
I think the code I am looking for is something like:
foreach my $val($fdx{_PARENT_OBJECT}{data}{"TEST_*"}{atm_rundatahistory}{"********"}{atm_prid}{content})
print("\n$val");
However I don't know the exact Syntax, and neither do I know which placeholders to set for "Test_10", since there are many tests numbers, e.g. "...Test_132...Test_134" and the Unix timestamps can be any 32 Bit Integer, so I guess I can use start as a placeholder? e.g. "********".
After some hours of searching on the web, I haven't found a understandable tutorial on how to access values from complex Perl hash structures, I guess there are some simple syntax-rules to know and you can get any value of even very complex data structures without to much effort.
I've already read perldoc_perlreftut. If there is any other easy to understandable tutorial for these kind of problems, please recommend them to me. I don't really know how I can learn to handle such complex data structures in Perl myself.

In Perl, what's the meaning of this code " has 'absolute_E' => (is => 'rw',default => sub {0} );"

The following codes make me so confused, I can't find any related knlowledge about the syntax "has ,is ,default, lazy". Can anybody make a detailed explain for me, best wishes.
has 'absolute_E' => (is => 'rw', default => sub {0} );
has 'retract_speed_mm_min' => (is => 'lazy');
has 'retract_speed_mm_min' => (is => 'lazy');
Judging by this line, this is probably a Moo class. To confirm this, have a look near the top of the file, and you should see something like use Moo.
Moo is an object-oriented framework for Perl. I'll assume you understand OO concepts.
Some historical background: Perl 5 has built-in OO capabilities, however it can get a little cumbersome at times. Then Moose came around as an improved way of OOP in Perl. But Moose was also quite heavy, with a compile-time cost, so Moo (and also Mouse just before it) came after that as something of a lighter-weight subset of Moose.
has is for defining attributes in your class.
has 'absolute_E' => ( is => 'rw', default => sub {0} );
This defines an attribute named absolute_E.
is => 'rw' means it is readable and writable, which means you can do this:
my $value = $obj->absolute_E; # gets the value
$obj->absolute_E($value); # sets the value
When you instantiate the object, you can supply a value for the attribute:
my $obj = My::Class->new( absolute_E => 5 );
But if you don't supply anything then absolute_E is set to 0 by default.
This second attribute has a few more things:
has 'retract_speed_mm_min' => (is => 'lazy');
This is short form for:
has 'retract_speed_mm_min' => (
is => 'ro',
lazy => 1,
builder => '_build_retract_speed_mm_min'
);
This attribute is readonly which means you can't change its value after construction. But you can supply a value at construction as before.
The builder is another way of providing a default value. It requires the class to have a separate method named _build_retract_speed_mm_min that should return the default value.
lazy works with builder. It means that the attribute should not be set by the builder until it the attribute is used. The delay may be used because the builder depends on other attributes in order to build this attribute's value.
There's a lot more in Moo and Moose. I would suggest reading http://modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html and https://metacpan.org/pod/Moose::Manual and https://metacpan.org/pod/Moo.
That code basically equals
has ('absolute_E', 'is', 'rw', 'default', sub {0} );
has ('retract_speed_mm_min', 'is', 'lazy');
And has looks like a user-defined subroutine.
=> is almost the same as ,:
The => operator is a synonym for the comma except that it causes a word on its left to be interpreted as a string if it begins with a letter or underscore and is composed only of letters, digits and underscores.

Puppet - requiring a defined resource with parameters

I'm very new to Puppet, and can't seem to find the answer to this question. I have a defined Puppet resource that takes a few arguments:
class xy::xy {
include apache:regular_apache
define setup($pkg_name, $xy_version, $pas_ver) {
file { '/etc/xy':
ensure => present,
notify => Service['apache'],
}
}
I'm trying to require this custom resource for another resource in another file.
class soft::buy {
include xy::xy
$xt_requires = [Xy::Xy::Setup["{'xt_buy': pkg_name => 'xt_buy_v01',
xy_version => '1.0.1',
pas_version => '2.1.4'}"]]
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => $xt_requires,
}
}
The error that I get is this: Syntax error at 'require'; expected '}'
From reading the Puppet docs, it seems like I'm missing a comma or colon somewhere, but I've tried a variety of things. I was wondering how to properly require a custom defined resource with parameters for another resource? Thanks!
The syntax error can be fixed by the following code snippet.
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => $xt_requires
}
[EDIT: The original code defines $xt_requires, not $requires]
You are defining the parameter require (which defines which resource needs to be handled first).
This is different from the language statement require (which is including a class and adding a dependency on the required class).
However, in the require-paramter, you cannot specify the parameters for the requirement, just it's presence. Fully correct would be:
xy::xy::setup {'xt_buy':
pkg_name => 'xt_buy_v01',
xy_version => '1.0.1',
pas_version => '2.1.4'
}
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => Xy::Xy::Setup['xt_buy']
}

Saving tree-like structure

I have following tree-like structure where each level is represented by separate class:
Book ------ Page ------ Line
1 n 1 n
Each class has a property that holds all its members (e/g/ Book::pages, as array of blessed refs), some properties specific to the level (e.g. Book::author) as well as some methods to add/remove its members.
Now I want to save/load all the data to/from a single file. It's not going to be a relational DB (most probably YAML will be used), so basically what I need to get at some point is something like:
my $book = {
author => "Fred Flinstone",
name => "My Favorite Stones",
pages => [
{
number => 1,
footer => "Dedicated to Wilma",
lines => [ ],
},
{
number => 2,
lines => [
{ text => "Preface", style => "h1" },
{ text => "This book is about my favorite stones:" },
{ text => "Marble" },
# ...
]
},
# ...
]
};
Should there be one smart pair of methods in Book that knows about all member classes? Or should each class implement part of it so that e.g. Line::save returned something like { text => "Marble" }?
What is the correct way to implement this? I would like solution that is as least as possible bounded to the actual data. What if I decide tomorrow to add Page::bookmarks and want to save Bookmarks as well?
Oh, and I'm using Moose, although that should not make much difference.
If you're using Moose, I would recommend looking at MooseX::Storage to handle this. You would use this module inside your package then add with Storage('format' => 'YAML', 'io' => 'File');.
This quick example may get you started:
package Book;
use Moose;
use MooseX::Storage;
with Storage('format' => 'YAML', 'io' => 'File');
...
1;
my $book = Book->new();
...
# to store object
$book->store('book.yml');
# to get object back
my $book2 = Book->load('book.yml');