Add puppet tuturials
Change-Id: Id882c83ead969eafdbdb8a2b3609dce862f8bab5
This commit is contained in:
parent
2da21774e6
commit
5975f7bc81
|
@ -1,8 +1,5 @@
|
|||
.. _develop:
|
||||
|
||||
Development Documentation
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
@ -12,3 +9,6 @@ Development Documentation
|
|||
develop/nailgun
|
||||
develop/alternatives
|
||||
develop/api_doc
|
||||
develop/module_structure
|
||||
develop/fuel_settings
|
||||
develop/puppet_tips
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
Using Fuel settings
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Fuel uses a special way to pass setting from Nailgun to Puppet manifests.
|
||||
Before the start of deployment process Astute uploads all settings, each
|
||||
server should have to the file */etc/astute.yaml* placed on every node.
|
||||
When Puppet is run facter reads this file entirely into a single fact
|
||||
*$astute_settings_yaml*. Then these settings are parsed by parseyaml function
|
||||
at the very beggining of site.pp file and set as rich data structure called
|
||||
*$fuel_settings*. All of the setting used during node deployment are stored
|
||||
there and can be used anywhere in Puppet code.
|
||||
For example, single top level variables are available as
|
||||
*$::fuel_settings['debug']*. More complex structures are also available as
|
||||
values of *$::fuel_settings* hash keys and can be accessed like usual hashes
|
||||
and arrays.
|
||||
There are also a lot of aliases and generated values that help you get needed
|
||||
values easier. You can always create variables from any of settings hash keys
|
||||
and work with this variable within your local scope or from other classes
|
||||
using fully qualified paths.::
|
||||
|
||||
$debug = $::fuel_settings['debug']
|
||||
|
||||
Some variables and structures are generated from settings hash by filtering
|
||||
and transformation functions. For example there is $node structure.::
|
||||
|
||||
$node = filter_nodes($nodes_hash, 'name', $::hostname)
|
||||
|
||||
It contains only settings of current node filtered from all nodes hash.
|
||||
|
||||
If you are going to use your module inside Fuel Library and need some
|
||||
settings you can just get them from this *$::fuel_settings* structure.
|
||||
Most variables related to network and OpenStack
|
||||
services configuration are already available there and you can use them as
|
||||
they are. But if your modules requires some additional or custom settings
|
||||
you'll have to either use **Custom Attributes** by editing json files before
|
||||
deployment, or, if you are integrating your project with Fuel Library, you
|
||||
should contact Fuel UI developers and ask them to add your configuration
|
||||
options to Fuel setting panel.
|
||||
|
||||
Once you have finished definition of all classes you need inside your module
|
||||
you can add this module's declaration either into the Fuel manifests such as
|
||||
*cluster_simple.pp* and *cluster_ha.pp* located inside
|
||||
*osnailyfacter/manifests* folder or to the other classes that are already
|
||||
being used if your additions are related to them.
|
||||
|
||||
Example module
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Let's demonstrate how to add new module to the Fuel Library by adding a simple
|
||||
class that will change terminal color of Red Hat based systems.
|
||||
Our module will be named *profile* and have only one class.::
|
||||
|
||||
profile
|
||||
profile/manifests
|
||||
profile/manifests/init.pp
|
||||
profile/files
|
||||
profile/files/colorcmd.sh
|
||||
|
||||
init.pp could have a class definition like this.::
|
||||
|
||||
class profile {
|
||||
if $::osfamily == 'RedHat' {
|
||||
file { 'colorcmd.sh' :
|
||||
ensure => present,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
path => "/etc/profile.d/colorcmd.sh",
|
||||
source => 'puppet:///modules/profile/colorcmd.sh',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
This class just downloads *colorcmd.sh* file and places it to the defined
|
||||
location if this class is run on Red Hat or CentOS system. The profile module
|
||||
can be added to Fuel modules by uploading its folder to */etc/puppet/modules*
|
||||
on the Fuel Master node.
|
||||
|
||||
Now we need to declare this module somewhere inside Fuel manifests. Since this
|
||||
module should be run on every server, we can use our main *site.pp* manifest
|
||||
found inside the *osnailyfacter/examples* folder. On the deployed master node
|
||||
this file will be copied to */etc/puppet/manifests* and used to deploy Fuel
|
||||
on all other nodes.
|
||||
The only thing we need to do here is to add *include profile* to the end of
|
||||
*/etc/puppet/manifests/site.pp* file on already deployed master node and to
|
||||
*osnailyfacter/examples/site.pp* file inside Fuel repository.
|
||||
|
||||
Declaring a class outside of node block will force this class to be included
|
||||
everywhere. If you want to include you module only on some nodes, you can add
|
||||
its declaration inside *cluster_simple* and *cluster_ha* classed to the blocks
|
||||
associated with required node's role.
|
||||
|
||||
You can add some additional logic to allow used to enable or disable this
|
||||
module from Fuel UI or at least by passing Custom Attributes to Fuel
|
||||
configuration.::
|
||||
|
||||
if $::fuel_settings['enable_profile'] {
|
||||
include 'profile'
|
||||
}
|
||||
|
||||
This block uses the *enable_profile* variable to enable or disable inclusion of
|
||||
profile module. The variable should be passed from Nailgun and saved to
|
||||
*/etc/astute.yaml* files of managed nodes.
|
||||
You can do it by either downloading settings files and manually editing
|
||||
them before deployment or by asking Fuel UI developers to include additional
|
||||
options to the settings panel.
|
|
@ -0,0 +1,288 @@
|
|||
Contributing to Fuel Library
|
||||
============================
|
||||
|
||||
This chapter will explain how to add new module or project into Fuel Library,
|
||||
how to integrate with other components
|
||||
and how to avoid different problems and potential mistakes. Fuel Library is a
|
||||
very big project and even experienced Puppet user will have problems
|
||||
understanding its structure and internal workings.
|
||||
|
||||
The Puppet modules structure
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
First let's start with Puppet modules structure. If you want to contribute you
|
||||
code into the Fuel Library it should be organized into a Puppet module. Modules
|
||||
are self-contained sets of Puppet code that usually are made to perform specific
|
||||
function. For example you could have a module for every service you are going
|
||||
to configure or for every part of your project. Usually it's a good idea to
|
||||
make a module independent but sometimes it could require or be required by
|
||||
other modules so module can be thinked about as a library.
|
||||
|
||||
The most important part of every Puppet module is its **manifests** folder.
|
||||
This folder contains Puppet classes and definitions which also contain
|
||||
resources managed by this module. Modules and classes also form namespaces.
|
||||
Each class or definition should be placed each into single file inside
|
||||
manifests folder and this file should be named same as class or definition.
|
||||
Module should have top level class that serves as a module's entry point and
|
||||
is named same as the module. This class should be placed into *init.pp* file.
|
||||
This example module shows the standard structure every Puppet module should
|
||||
follow.::
|
||||
|
||||
example
|
||||
example/manifests/init.pp
|
||||
example/manifests/params.pp
|
||||
example/manifests/client.pp
|
||||
example/manifests/server
|
||||
example/manifests/server/vhost.pp
|
||||
example/manifests/server/service.pp
|
||||
example/templates
|
||||
example/templates/server.conf.erb
|
||||
example/files
|
||||
example/files/client.data
|
||||
|
||||
The first file in manifests folder is named init.pp and should contain entry
|
||||
point class of this module. This class should be named same as our module.::
|
||||
|
||||
class example {
|
||||
|
||||
}
|
||||
|
||||
The second file is *params.pp*. These files are not mandatory but are often
|
||||
used to store different configuration values and parameters used by other
|
||||
classes of the module. For example, it could contain service name and package
|
||||
name of our hypothetical example module. There could be conditional statements
|
||||
if you need to change default values in different environments. *Params* class
|
||||
should be named as child to module's namespace as all other classes of the
|
||||
module.::
|
||||
|
||||
class example::params {
|
||||
$service = 'example'
|
||||
$server_package = 'example-server'
|
||||
$client_package = 'example-client'
|
||||
$server_port = '80'
|
||||
}
|
||||
|
||||
All other inside the manifests folder contain classes as well and can
|
||||
perform any action you might want to identify as a separate piece of code.
|
||||
This generally falls into sub-classes that don't require its users to
|
||||
configure the parameters explicitly, or possibly these are simply optional
|
||||
classes that are not required in all cases. In the following example,
|
||||
we create a client class to define a client package that will be installed,
|
||||
placed into a file called *client.pp*.::
|
||||
|
||||
class example::client {
|
||||
include example::params
|
||||
|
||||
package { $example::params::client_package :
|
||||
ensure => installed,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
As you can see we have used package name from params class. Consolidating
|
||||
all values that might require editing into a single class, as opposed to
|
||||
hardcoding them, allows you to reduce the effort required to maintain and
|
||||
develop the module further in the future. If you are going to use any values
|
||||
from params class you should not forget to include it first to force its
|
||||
code to execute and create all required variables.
|
||||
|
||||
You can add more levels into the namespace structure if you want. Let's create
|
||||
server folder inside our manifests folder and add *service.pp* file there. It
|
||||
would be responsible for installation and running server part of our imaginary
|
||||
software. Placing the class inside subfolder adds one level into name of
|
||||
contained class.::
|
||||
|
||||
class example::server::service (
|
||||
$port = $example::params::server_port,
|
||||
) inherits example::params {
|
||||
|
||||
$package = $example::params::server_package
|
||||
$service = $example::params::service
|
||||
|
||||
package { $package :
|
||||
ensure => installed,
|
||||
}
|
||||
|
||||
service { $service :
|
||||
ensure => running,
|
||||
enabled => true,
|
||||
hasstatus => true,
|
||||
hasrestart => true,
|
||||
}
|
||||
|
||||
file { 'example_config' :
|
||||
ensure => present,
|
||||
path => '/etc/example.conf',
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
content => template('example/server.conf.erb'),
|
||||
}
|
||||
|
||||
file { 'example_config_dir' :
|
||||
ensure => directory,
|
||||
path => '/etc/example.d',
|
||||
owner => 'example',
|
||||
group => 'example',
|
||||
mode => '0755',
|
||||
}
|
||||
|
||||
Package[$package] -> File['example_config', 'example_config_dir'] ~>
|
||||
Service['example_config']
|
||||
|
||||
}
|
||||
|
||||
This example is a bit more complex. Let's see what it does.
|
||||
|
||||
Class *example::server::service* is **parameterized** and can accept one
|
||||
parameter - port to which server process should bind to. It also uses a popular
|
||||
"smart defaults" hack. This class inherits the params class and uses its values
|
||||
default only if no port parameter is provided. In this case, you can't use
|
||||
*include params* to load the default values because it's called by the
|
||||
*inherits example::params* clause of the class definition.
|
||||
|
||||
Then inside our class we take several variable from params class and declare
|
||||
them as variable of the local scope. This is conveniency hack to make their
|
||||
names shorter.
|
||||
|
||||
Next we declare our resources. These resources are package, service, config
|
||||
file and config dir. Package resource will install package which name is taken
|
||||
from variable if it's not already installed. File resources create config file
|
||||
and config dir and service resource would start the daemon process and enable
|
||||
its autostart.
|
||||
|
||||
And the last but not least part of this class is *dependency* declaration. We
|
||||
have used "chain" syntax to specify the order of evaluation of these
|
||||
resources. Of course it's important first to install package, then
|
||||
configuration files and only then start the service. Trying to start service
|
||||
before installing package will definitely fail. So we need to tell Puppet that
|
||||
there are dependencies between our resources.
|
||||
|
||||
The arrow operator that has a tilde instead of a minus sign (~>) means not
|
||||
only dependency relationship but also *notifies* the object to the right
|
||||
of the arrow to refresh itself. In our case any changes in configuration
|
||||
file would make the service to restart and load new configuration file.
|
||||
Service resource react to notification event by restating managed service.
|
||||
Other resources may perform different actions instead if they support it.
|
||||
|
||||
Ok, but where do we get our configuration file content from? It's generated by
|
||||
template function. Templates are text files with Ruby's erb language tags that
|
||||
are used to generate needed text file using pre-defined text and some
|
||||
variables from manifest.
|
||||
|
||||
These template files are located inside the **templates** folder of the
|
||||
module and usually have *erb* extension. Calling template function with
|
||||
template name and module name prefix will try to load this template and
|
||||
compile it using variables from the local scope of the class function was
|
||||
called from. For example we want to set bind port of our service in its
|
||||
configuration file so we write template like this and save it inside
|
||||
templates folder as server.conf.erb file.::
|
||||
|
||||
bind_port = <%= @port %>
|
||||
|
||||
Template function will replace 'port' tag with value of port variable from our
|
||||
class during Puppet's catalog compilation.
|
||||
|
||||
Ok, now we have our service running and client package installed. But what if
|
||||
our service needs several virtual hosts? Classes cannot be declared several
|
||||
times with different parameters so it's where **definitions** come to the
|
||||
rescue. Definitions are very similar to classes, but unlike classes, they
|
||||
have titles like resources do and can be used many times with different
|
||||
title to produce many instances of managed resources. Defined types can
|
||||
also accept parameters like parametrized classes do.
|
||||
|
||||
Definitions are placed in single files inside manifests directories same as
|
||||
classes and are similarly named using namespace hierarchy.
|
||||
Let's create our vhost definition.::
|
||||
|
||||
define example::server::vhost (
|
||||
$path = '/var/data',
|
||||
) {
|
||||
include example::params
|
||||
|
||||
$config = “/etc/example.d/${title}.conf”
|
||||
$service = $example::params::service
|
||||
|
||||
file { $config :
|
||||
ensure => present,
|
||||
owner => 'example',
|
||||
group => 'example',
|
||||
mode => '0644',
|
||||
content => template('example/vhost.conf.erb'),
|
||||
}
|
||||
|
||||
File[$config] ~> Service[$service]
|
||||
}
|
||||
|
||||
This defined type only creates a file resource with its name populated
|
||||
by the title used when it gets defined and sets notification relationship
|
||||
with service to make it restart when vhost file is changed.
|
||||
|
||||
This defined type can be used by other classes like a simple resource type to
|
||||
create as many vhost files as we need.::
|
||||
|
||||
example::server::vhost { 'mydata' :
|
||||
path => '/path/to/my/data',
|
||||
}
|
||||
|
||||
Defined types can form relationships in a same way as resources do but you
|
||||
need to capitalize all elements of path to make reference.::
|
||||
|
||||
File['/path/to/my/data'] -> Example::Server::Vhost['mydata']
|
||||
|
||||
Now we can work with text files using templates but what if we need to manage
|
||||
binary data files? Binary files or text files that will always be same can be
|
||||
placed into **files** directory of our module and then be taken by file
|
||||
resource.
|
||||
|
||||
Let's imagine that our client package need some binary data file we need to
|
||||
redistribute with it. Let's add file resource to our *example::client* class.::
|
||||
|
||||
file { 'example_data' :
|
||||
path => '/var/lib/example.data',
|
||||
owner => 'example',
|
||||
group => 'example',
|
||||
mode => '0644',
|
||||
source => 'puppet:///modules/example/client.data',
|
||||
}
|
||||
|
||||
We have specified source as a special puppet URL scheme with module's and
|
||||
file's name. This file will be placed to specified location during puppet run.
|
||||
But on each run Puppet will check this files checksum overwriting it if it
|
||||
changes so don't use this method with mutable data. Puppet's fileserving works
|
||||
both in client-server and masterless modes but unlike template requires
|
||||
managed server to have access to master during catalog run because file's
|
||||
content is not included into catalog.
|
||||
|
||||
Ok, we have all classes and resources we need to manage our hypothetical
|
||||
example service. Let's try to put everything together. Our example class
|
||||
defined inside *init.pp* is still empty so we can use it to declare all other
|
||||
classes.::
|
||||
|
||||
class example {
|
||||
include example::params
|
||||
include example::client
|
||||
|
||||
class { 'example::server::service' :
|
||||
port => '100',
|
||||
}
|
||||
|
||||
example::server::vhost { 'site1' :
|
||||
path => '/data/site1',
|
||||
}
|
||||
|
||||
example::server::vhost { 'site2' :
|
||||
path => '/data/site2',
|
||||
}
|
||||
|
||||
example::server::vhost { 'test' :
|
||||
path => '/data/test',
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Now we have entire module packed inside *example* class and we can just
|
||||
include this class to any node where we want to see our service running.
|
||||
Declaration of parametrized class also did override default port number from
|
||||
params file and we have three separate virtual hosts for out service. Client
|
||||
package is also included into this class.
|
|
@ -0,0 +1,316 @@
|
|||
Resource duplication and file conflicts
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have been developing your module that somehow uses services which are
|
||||
already in use by other components of OpenStack, most likely you will try to
|
||||
declare some of the same resources that have already been declared.
|
||||
Puppet architecture doesn't allow declaration of resources that have same
|
||||
type and title even if they do have same attributes.
|
||||
|
||||
For example, your module could be using Apache and has Service['apache']
|
||||
declared. When you are running your module outside Fuel nothing else tries to
|
||||
control this service to and everything work fine. But when you will try to add
|
||||
this module to Fuel you will get resource duplication error because Apache is
|
||||
already managed by Horizon module.
|
||||
|
||||
There is pretty much nothing you can do about this problem because uniqueness
|
||||
of Puppet resources is one on its core principles. But you can try to solve
|
||||
the problem by one of following ways.
|
||||
|
||||
The best thing you can do is to try to use an already declared resource by
|
||||
settings dependencies to the other class that does use it. This will not work
|
||||
in many cases and you may have to modify both modules or move conflicting
|
||||
resource elsewhere to avoid conflicts.
|
||||
|
||||
Puppet does provide a good solution to this problem - **virtual resources**.
|
||||
The idea behind it is that you move resource declaration to separate class and
|
||||
make them virtual. Virtual resources will not be evaluated until you realize
|
||||
them and you can do it in all modules that do require this resources.
|
||||
The trouble starts when these resources have different attributes and complex
|
||||
dependencies. Most current Puppet modules doesn't use virtual resources and
|
||||
will require major refactoring to add them.
|
||||
|
||||
Puppet style guidelines advise to move all classes related with the same
|
||||
service inside a single module instead of using many modules to work with
|
||||
same service to minimize conflicts, but in many cases this approach
|
||||
doesn't work.
|
||||
|
||||
There are also some hacks such are defining resource inside *if !
|
||||
defined(Service['apache']) { ... }* block or using **ensure_resource**
|
||||
function from Puppet's stdlib.
|
||||
|
||||
Similiar problems often arise then working with configuration files.
|
||||
Even using templates doesn't allow several modules to directly edit save
|
||||
file. There are a number of solutions to this starting from using
|
||||
configurations directories and snippets if service supports them to
|
||||
representing lines or configuration options as resources and managing
|
||||
them instead of entire files.
|
||||
|
||||
Many services does support configuration directories where you can place
|
||||
configuration files snippets. Daemon will read them all, concatinate and
|
||||
use like it was a single file. Such services are the most convinient to
|
||||
manage with Puppet. You can just separate you configuration mand manage
|
||||
its pieces as templates. If your service doesn't know how to work with
|
||||
snippets you still can use them. You only need to create parts of your
|
||||
configuration file in some directory and then just combine them all
|
||||
using simple exec with *cat* command. There is also a speciall *concat*
|
||||
resource type to make this approach easier.
|
||||
|
||||
Some configuration files could have stamdard structure and can be managed
|
||||
by custom resource types. For example, there is the *ini_file* resource
|
||||
type to manage values in compatible configuration as single resources.
|
||||
There is also *augeas* resource type that can manage many popular
|
||||
configuration file formats.
|
||||
|
||||
Each approach has its own limitations and editing single file from
|
||||
many modules is still non-trivial task in most cases.
|
||||
|
||||
Both resource duplication and file editing problems doesn't have a good
|
||||
solution for every possible case and significantly limit possibility
|
||||
of code reuse.
|
||||
|
||||
The last approach to solving this problem you can try is to modify files
|
||||
by scripts and sed patches ran by exec resources. This can have unexpected
|
||||
results because you can't be sure of what other operations are performed
|
||||
on this configuration file, what text patterns exist there, and if your
|
||||
script breaks another exec.
|
||||
|
||||
Puppet module containment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Fuel Library consists of many modules with a complex structure and
|
||||
several dependencies defined between the provided modules.
|
||||
There is a known Puppet problem related to dependencies between
|
||||
resources contained inside classes declared from other classes.
|
||||
If you declare resources inside a class or definition they will be
|
||||
contained inside it and entire container will not be finished until all
|
||||
of its contents have been evaluated.
|
||||
|
||||
For example, we have two classes with one notify resource each.::
|
||||
|
||||
class a {
|
||||
notify { 'a' :}
|
||||
}
|
||||
|
||||
class b {
|
||||
notify { 'b' :}
|
||||
}
|
||||
|
||||
Class['a'] -> Class['b']
|
||||
|
||||
include a
|
||||
include b
|
||||
|
||||
Dependencies between classes will force contained resources to be executed in
|
||||
declared order.
|
||||
But if we add another layer of containers dependencies between them will not
|
||||
affect resources declared in first two classes.::
|
||||
|
||||
class a {
|
||||
notify { 'a' :}
|
||||
}
|
||||
|
||||
class b {
|
||||
notify { 'b' :}
|
||||
}
|
||||
|
||||
class l1 {
|
||||
include a
|
||||
}
|
||||
|
||||
class l2 {
|
||||
include b
|
||||
}
|
||||
|
||||
Class['l1'] -> Class['l2']
|
||||
|
||||
include 'l1'
|
||||
include 'l2'
|
||||
|
||||
This problem can lead to unexpected and in most cases unwanted behaviour
|
||||
when some resources 'fall out' from their classes and can break the logic
|
||||
of the deployment process.
|
||||
|
||||
The most common solution to this issue is **Anchor Pattern**. Anchors are
|
||||
special 'do-nothing' resources found in Puppetlab's stdlib module.
|
||||
Anchors can be declared inside top level class and be containd
|
||||
inside as any normal resource. If two anchors was declared they can be
|
||||
named as *start* and *end* anchor. All classes, that should be contained
|
||||
inside the top-level class can have dependencies with both anchors.
|
||||
If a class should go after the start anchor and before the end anchor
|
||||
it will be locked between them and will be correctly containd inside
|
||||
the parent class.::
|
||||
|
||||
class a {
|
||||
notify { 'a' :}
|
||||
}
|
||||
|
||||
class b {
|
||||
notify { 'b' :}
|
||||
}
|
||||
|
||||
class l1 {
|
||||
anchor { 'l1-start' :}
|
||||
include a
|
||||
anchor { 'l1-end' :}
|
||||
|
||||
Anchor['l1-start'] -> Class['a'] -> Anchor['l1-end']
|
||||
}
|
||||
|
||||
class l2 {
|
||||
anchor { 'l2-start' :}
|
||||
include b
|
||||
anchor { 'l2-end' :}
|
||||
|
||||
Anchor['l2-start'] -> Class['b'] -> Anchor['l2-end']
|
||||
}
|
||||
|
||||
Class['l1'] -> Class['l2']
|
||||
|
||||
include 'l1'
|
||||
include 'l2'
|
||||
|
||||
This hack does help to prevent resources from randomly floating out of their
|
||||
places, but look very ugly and is hard to understand. We have to use this
|
||||
technique in many of Fuel modules which are rather complex and require such
|
||||
containment.
|
||||
If your module is going to work with dependency scheme like this, you could
|
||||
find anchors useful too.
|
||||
|
||||
There is also another solution found in the most recent versions of Puppet.
|
||||
*Contain* function can force declared class to be locked within its
|
||||
container.::
|
||||
|
||||
class l1 {
|
||||
contain 'a'
|
||||
}
|
||||
|
||||
class l2 {
|
||||
contain 'b'
|
||||
}
|
||||
|
||||
Puppet scope and variables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The way Puppet looks for values of variables from inside classes can be
|
||||
confusing too. There are several levels of scope in Puppet.
|
||||
**Top scope** contains all facts and built-in variables and goes from the
|
||||
start of *site.pp* file before any class or node declaration. There is also a
|
||||
**node scope**. It can be different for every node block. Each class and
|
||||
definition start their own **local scopes** and their variables and resource
|
||||
defaults are available their. **They can also have parent scopes**.
|
||||
|
||||
Reference to a variable can consist of two parts
|
||||
**$(class_name)::(variable_name)** for example *$apache::docroot*. Class name
|
||||
can also be empty and such record will explicitly reference top level scope
|
||||
for example *$::ipaddress*.
|
||||
|
||||
If you are going to use value of a fact or top-scope variable it's usually a
|
||||
good idea to add two colons to the start of its name to ensure that you
|
||||
will get the value you are looking for.
|
||||
|
||||
If you want to reference variable found in another class and use fully
|
||||
qualified name like this *$apache::docroot*. But you should remember that
|
||||
referenced class should be already declared. Just having it inside your
|
||||
modules folder is not enough for it. Using *include apache* before referencing
|
||||
*$apache::docroot* will help. This technique is commonly used to make
|
||||
**params** classes inside every module and are included to every other class
|
||||
that use their values.
|
||||
|
||||
And finally if you reference a local variable you can write just *$myvar*.
|
||||
Puppet will first look inside local scope of current class of defined type,
|
||||
then inside parent scope, then node scope and finally top scope. If variable
|
||||
is found on any of this scopes you get the first match value.
|
||||
|
||||
Definition of what the parent scope is varies between Puppet 2.* and Puppet
|
||||
3.*. Puppet 2.* thinks about parent scope as a class from where current class
|
||||
was declared and all of its parents too. If current class was inherited
|
||||
from another class base class also is parent scope allowing to do popular
|
||||
*Smart Defaults* trick.::
|
||||
|
||||
class a {
|
||||
$var = ‘a’
|
||||
}
|
||||
|
||||
class b(
|
||||
$a = $a::var,
|
||||
) inherits a {
|
||||
|
||||
}
|
||||
|
||||
Puppet 3.* thinks about parent scope only as a class from which current class
|
||||
was inherited if any and doesn't take declaration into account.
|
||||
|
||||
For example::
|
||||
|
||||
$msg = 'top'
|
||||
|
||||
class a {
|
||||
$msg = "a"
|
||||
}
|
||||
|
||||
class a_child inherits a {
|
||||
notify { $msg :}
|
||||
}
|
||||
|
||||
Will say 'a' in puppet 2.* and 3.* both. But.::
|
||||
|
||||
$msg = 'top'
|
||||
|
||||
class n1 {
|
||||
$msg = 'n1'
|
||||
include 'n2'
|
||||
}
|
||||
|
||||
class n2 {
|
||||
notify { $msg :}
|
||||
}
|
||||
|
||||
include 'n1'
|
||||
|
||||
Will say 'n1' in puppet 2.6, will say 'n1' and issue *deprication warning* in
|
||||
2.7, and will say 'top' in puppet 3.*
|
||||
|
||||
Finding such variable references replacing them with fully qualified names is
|
||||
very important part Fuel of migration to Puppet 3.*
|
||||
|
||||
Where to find more information
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The best place to start learning Puppet is Puppetlabs' official learning
|
||||
course (http://docs.puppetlabs.com/learning/). There is also a special virtual
|
||||
machine image you can use to safely play with Puppet manifests.
|
||||
|
||||
Then you can continue to read Puppet reference and other pages of Puppetlabs
|
||||
documentation.
|
||||
|
||||
You can also find a number of printed book about Puppet and how to use it to
|
||||
manage your IT infrastructure.
|
||||
|
||||
Pro Puppet
|
||||
http://www.apress.com/9781430230571
|
||||
|
||||
Pro Puppet. 2nd Edition
|
||||
http://www.apress.com/9781430260400
|
||||
|
||||
Puppet 2.7 Cookbook
|
||||
http://www.packtpub.com/puppet-2-7-for-reliable-secure-systems-cloud-computing-
|
||||
cookbook/book
|
||||
|
||||
Puppet 3 Cookbook
|
||||
http://www.packtpub.com/puppet-3-cookbook/book
|
||||
|
||||
Puppet 3: Beginners Guide
|
||||
http://www.packtpub.com/puppet-3-beginners-guide/book
|
||||
|
||||
Instant Puppet 3 Starter
|
||||
http://www.packtpub.com/puppet-3-starter/book
|
||||
|
||||
Pulling Strings with Puppet Configuration Management Made Easy
|
||||
http://www.apress.com/9781590599785
|
||||
|
||||
Puppet Types and Providers Extending Puppet with Ruby
|
||||
http://shop.oreilly.com/product/0636920026860.do
|
||||
|
||||
Managing Infrastructure with Puppet. Configuration Management at Scale
|
||||
http://shop.oreilly.com/product/0636920020875.do
|
|
@ -81,14 +81,14 @@ Details on Cluster Provisioning & Deployment (via Facter extension)
|
|||
Astute --> Naily: provisioned
|
||||
Naily --> Nailgun: provisioned
|
||||
Nailgun --> WebUser: status on UI
|
||||
Astute -> MC: Create /etc/naily.facts
|
||||
Astute -> MC: Create /etc/astute.yaml
|
||||
|
||||
Astute -> MC: run puppet
|
||||
MC -> Puppet: runonce
|
||||
Puppet -> Puppet_master: get modules,class
|
||||
Puppet_master --> Puppet: modules, class
|
||||
Puppet -> Facter: get facts
|
||||
Facter --> Puppet: set of facts
|
||||
Facter --> Puppet: set facts and parse astute.yaml
|
||||
|
||||
Puppet -> Puppet: applies $role
|
||||
Puppet --> MC: done
|
||||
|
@ -97,23 +97,27 @@ Details on Cluster Provisioning & Deployment (via Facter extension)
|
|||
Naily --> Nailgun: deploy is done
|
||||
Nailgun --> WebUser: deploy is done
|
||||
|
||||
Once deploy and provisioning messages are accepted by Naily, provisioining method is called in Astute.
|
||||
Provisioning part creates system in Cobbler and calls reboot over Cobbler. Then
|
||||
Astute uses `MCollective direct addressing mode <http://www.devco.net/archives/2012/06/19/mcollective-direct-addressing-mode.php>`_
|
||||
to check if all required nodes are available,
|
||||
include puppet agent on them. If some nodes are not ready yet, Astute waits for a few seconds and does request again.
|
||||
When nodes are booted in target OS,
|
||||
Astute uses naily_fact MCollective plugin to post data to a special file /etc/naily.fact on target system.
|
||||
Data include role and all other variables needed for deployment. Then, Astute calls puppetd MCollective plugin
|
||||
to start deployment. Puppet is started on nodes, and requests Puppet master for modules and manifests.
|
||||
site.pp on Master node defines one common class for every node.
|
||||
Accordingly, puppet agent starts its run. Modules contain facter extension, which runs before deployment. Extension
|
||||
reads facts from /etc/naily.fact placed by mcollective, and extends Facter data with these facts, which can be
|
||||
easily used in Puppet modules. Case structure in running class chooses appropriate class to import, based on $role
|
||||
variable, received from /etc/naily.fact. It loads and starts to execute. All variables from file are available
|
||||
like ordinary facts from Facter.
|
||||
|
||||
It is possible to use the system without Nailgun and Naily: user creates a YAML file with all required
|
||||
data, and calls Astute binary script. Script loads data from YAML and instantiates Astute instance
|
||||
the same way as it's instanciated from Naily.
|
||||
Once deploy and provisioning messages are accepted by Naily, provisioining
|
||||
method is called in Astute. Provisioning part creates system in Cobbler and
|
||||
calls reboot over Cobbler. Then Astute uses `MCollective direct addressing
|
||||
mode
|
||||
<http://www.devco.net/archives/2012/06/19/mcollective-direct-addressing-mode.ph
|
||||
p>`_
|
||||
to check if all required nodes are available, include puppet agent on them. If
|
||||
some nodes are not yet ready, Astute waits for a few seconds and tries to
|
||||
request again. When nodes are booted in target OS, Astute uses upload_file
|
||||
MCollective plugin to push data to a special file */etc/astute.yaml* on the
|
||||
target system.
|
||||
Data include role and all other variables needed for deployment. Then, Astute
|
||||
calls puppetd MCollective plugin to start deployment. Puppet is started on
|
||||
nodes, and requests Puppet master for modules and manifests. *site.pp* on
|
||||
Master node defines one common class for every node.
|
||||
|
||||
Accordingly, puppet agent starts its run. Modules contain facter extension,
|
||||
which runs before deployment. Extension reads data from */etc/astute.yaml*
|
||||
placed by mcollective, and extends Facter data with it as a single fact, which
|
||||
is then parsed by *parseyaml* function to create *$::fuel_settings* data
|
||||
structure. This structure contains all variables as a single hash and
|
||||
supports embedding of other rich structures such as nodes hash or arrays.
|
||||
Case structure in running class chooses appropriate class to import,
|
||||
based on *role* and *deployment_mode* variables found in */etc/astute.yaml*.
|
||||
|
|
Loading…
Reference in New Issue