I am using https://github.com/openstack/puppet-keystone to set up an OpenStack management/controller node. I need to add the 'glance' user to keystone. I want to try and do as much as I can in my hiera data so my manifest will be simple.
Here is my manifest:
class kilo2_keystone {
include controller_ceph
include keystone
include keystone::config
include keystone::user
# keystone_user { 'glance':
# ensure => present,
# }
}
The commented out section works, but I want to be able to do include keystone::user and supply the parameters in my hiera data like so:
keystone::user:
"%{hiera('glance_admin_user')}":
ensure: present
But when I run puppet agent -t on my node I get this error:
Could not find class ::keystone::user
The commented-out code declares a resource of type keystone_user, not a class. Presumably its type, keystone_user, is provided by the puppet-keystone module. The include() family of functions are for declaring classes, not resources, so they are inapplicable to keystone_user.
There is more than one way you could proceed. If you don't anticipate wanting to anything more complicated than declaring one or more keystone_users present, then I'd recommend giving your class a parameter for the user name(s), to which you can assign a value via Hiera:
class kilo2_keystone($usernames = []) {
include controller_ceph
include keystone
include keystone::config
keystone_user { $usernames:
ensure => present,
}
}
On the other hand, if you want to be able to declare multiple users, each with its own set of attributes, then the create_resources() function is probably the path of least resistance. You still want to parameterize your class so that it gets the data from Hiera via automated data binding, but now you want the data to be structured differently, as described in the create_resources() docs: as a hash mapping resource titles (usernames, in your case) to inner hashes of resource parameters to corresponding values.
For example, your class might look like this:
class kilo2_keystone($userdata = {}) {
include controller_ceph
include keystone
include keystone::config
create_resources('keystone_user', $userdata)
}
The corresponding data for this class might look like this:
kilo2_keystone::userdata:
glance:
ensure: present
enabled: true
another_user:
ensure: absent
Note also that you are placing your kilo2_keystone class in the top scope. You really ought to put it in a module and assign it to that module's namespace. The latter would look like this:
class mymodule::kilo2_keystone($userdata = {}) {
# ...
}
Related
I am having some trouble using module hiera data.
module: /etc/puppetlabs/code/environments/production/modules/usehiera
tree structure:
usehiera
usehiera/hiera.yaml
usehiera/data
usehiera/data/common.yaml
usehiera/manifests
usehiera/manifests/init.pp
hiera.yaml:
---
version: 5
defaults:
datadir: data
data_hash: yaml_data
hierarchy:
- name: 'common'
- path: 'common.yaml'
data/common.yaml:
---
usehiera::apples: 'this is some data'
manifests/init.pp:
class usehiera{
file{'/tmp/hiera_lookup.txt':
ensure => present,
#content => hiera('oranges') #this works with global hiera
content => $apples
}
}
As you can see I seem to have the global hiera working with "hiera('oranges')" when I run this module on my node. When I try to use the module hiera data the puppet run finishes successfully but hiera_lookup.txt is just empty.
Steps I have taken to troubleshoot:
restart puppetserver after hiera changes
try using $usehira::apples
try using hiera('apples')
moving my hiera.yaml inside data/
using lookup with --explain doesnt really give me anything useful, just says lookup() not found
Can anyone help me out? I have been stuck with this for a decent amount of time and not sure what the issue could be.
As #MattSchuchard pointed out in comments, your hiera.yaml is inappropriately formed. The documentation contains examples.
But a bigger issue seems to be incorrect expectations. Evidently, you assume that ordinary class variable $usehiera::apples will automatically take the value associated with the corresponding key in the module-level hiera data, but that simply is not the case. Hiera data -- whether global, environment-level, or module-level -- is automatically bound to class parameters, but not to other class variables.
You can set ordinary class variables from hiera data via explicit lookup:
# the hiera() function is deprecated; use lookup():
$apples = lookup('usehiera::apples')
Alternatively, you can make $apples a class parameter:
class usehiera(String $apples) {
file{'/tmp/hiera_lookup.txt':
ensure => 'present',
content => $apples,
}
}
Note well that if you make it a parameter then its value can also be customized via a resource-like class declaration, which takes precedence over your Hiera data.
Note also that the difference between global, per-environment, and module-specific Hiera data is just scope and precedence, not functionality.
I have a module "base" with an init.pp class which has some parameters as such:
class base (
$listen_ip = "xx.xx.xx.xx",
$listen_port = 3306,
$admin_username = 'admin',
$admin_password = 'admin',
)
{
...
}
Then I have created a profile "base" where I want to set some of the parameters:
class profile::base {
class { 'base':
$listen_ip = "xxx.xxx.xx.xx",
$listen_port => 6033,
}
}
Then the is a secondary profile where I want to set the username and password:
class profile::department::sales::base {
class { '::profile::base':
$admin_username = "some_user",
$admin_password => "some_pw",
}
}
However it's not possible to set the parameters from the "sales" profile.
The idea is that some values will be always the same for the base class and that some differ based on the department.
However it's not possible to set the parameters from the "sales" profile.
Not exactly. What is not allowed is using two different resource-like declarations for the same class while building one manifest. If you use even one then you must make certain that it is the first (or only) declaration of that class that the catalog builder evaluates.
To understand this, you need to appreciate that assigning parameter values is not the principal purpose of declarations such you are using. The principal purpose is rather to specify that the class in question should be included in the catalog in the first place. In service to that goal, values are bound to all the parameters of a class at the point where its first declaration is evaluated. Thus, your two class declarations do not supplement each other. Instead, they conflict with each other.
Even if the parameter values it specified for class base were identical to those declared by class profile::base, however, Puppet would still object to all uses of class profile::department::sales::base. To simplify evaluation and be absolutely certain to avoid inconsistency, it implements a stronger constraint than is actually required: that only the first-evaluated declaration of any given class may be a resource-like one.
Note: the latest docs actually specify an even stronger constraint than that: "Resource-like class declarations require that you declare a given class only once." In practice, however, this is a simplification (in every version of Puppet so far released since the introduction of parameterized classes). It is likely inspired by the fact that the order in which Puppet manifests are evaluated can be difficult to predict, so if you use include-like declarations along with a resource-like declaration of the same class, in different manifests, then it can be hard to ensure that the resource-like one is always evaluated first.
The idea is that some values will be always the same for the base
class and that some differ based on the department.
For most purposes it is best to avoid resource-like class declarations altogether, relying instead on external data (Hiera) for binding values to class parameters. Hiera recognizes a hierarchy of data sources (hence the name) and supports specifying different parameters at different levels, and even overriding data from one level at a higher-priority level.
My suggestion, then, is to leverage Hiera to assign appropriate parameter values to class base. There are many ways the specifics could play out.
I have a user resource in a module that gets used by several different nodes. Now I want to add this user to a group but only in one specific node. Is there a good solution for this?
Module looks something like this:
class testmodule::basics {
user { 'testuser':
ensure => present,
home => '/home/testuser',
managehome => true,
}
}
Node manifest:
node 'testnode' {
include testmodule::basics
# here I would like to add the user to a group
# something like this (obviously does not work because of duplicate resource)
user { 'testuser':
groups => 'testgroup',
membership => 'minimum',
}
}
You have several alternatives, split among several general categories.
Category 1 - use external data to communicate which secondary groups the user should have. The particular datum might be a flag to indicate whether the user should be in the secondary group, or it might be an actual array of the appropriate secondary groups. You might then obtain it either by directly calling the lookup() or hiera() function, depending on which version of Puppet you are using, or by creating a class parameter for it, and using automatic data binding.
Example:
modules/testmodule/manifests/basics.pp:
class testmodule::basics($secondary_groups = []) {
user { 'testuser':
ensure => present,
home => '/home/testuser',
managehome => true,
groups => $secondary_groups
}
}
data/nodes/special.my.com.yaml:
---
testmodule::basics::secondary_groups:
- testgroup
Category 2 - Set up a class parameter to receive the distinguishing data, just as in one of the category 1 options, and feed the data in via an external node classifier (ENC), instead of external data. Setting up and enabling an ENC has much broader implications than feeding data to a single class, however, so I don't really recommend this unless you are already using or planning to use an ENC.
Category 3 - Perform a resource parameter override where needed. This could be almost a drop-in change to your example manifest, though it would be better to put the override in a separate class than to perform it directly in the node block. In a class that inherits from testmodule::basics, you can use resource parameter override syntax, like so:
modules/testmodule/manifests/basics/special.pp:
class testmodule::basics::special inherits testmodule::basics {
User['testuser'] {
groups => 'testgroup'
}
}
If you want to perform such an override in a node block or in an unrelated class, however, then you need to do it via a collector:
node 'testnode' {
include testmodule::basics
User<title == 'testuser'> {
groups => 'testgroup'
}
}
To two varieties of overrides have some subtle differences beyond the scopes in which they may be used, so do read the docs for more information.
In the scope of one class, I need to be able to access a variable from another class. The variable is passed as a parameter, e.g.
class parameterized_class (
$param1,
) {
...
}
and
class other_class () {
include parameterized_class
Class['parameterized_class'] -> Class['other_class']
$local_var = $parameterized_class::param1
}
With an example usage of:
node default {
class { 'parameterized_class':
param1 => 'somevalue',
}
class { 'other_class': }
}
The above example does not work, as I get an error that looks roughly like:
Must pass param1 to
Class[Parameterized_class] at
/path/to/modules/parameterized_class/manifests/init.pp:1
on node localhost
Obviously, include is trying to declare parameterized_class without passing any parameters. But from the docs, I can see that include allows for a class to have been already previously declared, and since I have the parameterized_class declaration as a dependency of other_class, I don't understand how I could be getting this error.
I'm using Puppet 3.4.3, the version available on Ubuntu 14.04 Trusty
How should I go about retrieving the value of the $param1 in parameterized_class from within other_class's scope? Is it not possible to use include on parameterized classes?
According to the Puppet documentation (https://docs.puppet.com/puppet/3.5/lang_classes.html#include-like-behavior) you cannot use include-like declarations with mandatory parameters. In your case, what you can do is just not bother with the include since you've already handled that by declaring it in your node definition. Also, because both are declared in your node definition you would want to order them there as well.
As a side note not providing default values is a bad practice as is using variables across modules.
I want to pass node specific information to a class, which then could evaluate it for specific purposes. Actually this question consists of three parts.
Say, I have the following node:
node 'devbox' {
$serverType = 'something'
include someClass
someOtherClass { 'someOtherClass':
par1 => 'value',
}
targetClass { 'nodeInformationShouldGoHere': }
}
Inside targetClass, I want to evaluate if serverType, someClass or someOtherClass is set (e.g. with if-else). My questions now are:
Is setting and passing the variable suitable in puppet for this?
or should I use tags (as the classes are automatically tagged for this node)?
Are their further approaches and what are limitations to above ones (e.g. do they work for resource types?)?
You can absolutely use puppet this way. Read over the documentation for Parameterized Classes and see if that meets your needs.