is it possible to create a system group from hiera?
I know how to create a system group in the manifest via class, but I have failed to move that to hiera
Any example of creating a systemgroup "foo" with gid "1000" in hiera would be highly appreciated.
Thanks
Hiera is a data service. It can provide data about system groups, or about any kind of resource at all, but you need at least a little bit of manifest code somewhere to make a resource declaration out of that. For example, this pushes all the details out of the manifest and into Hiera (or other manifest code):
class mymodule::groups(Hash[String, Hash] $groupdata) {
$groupdata.each |$gname, $params| {
group { $gname: * => $params }
}
}
Hiera data feeding that class might look something like this:
mymodule::groups::groupdata:
group1:
system: true
group2:
gid: 42
system: true
group3:
ensure: absent
The value for $groupdata is, as declared by the class, a Hash of Hashes. The keys are group names, and the values are hashes of property names and values for the built-in Group resource type.
There are many other ways that one could accomplish this, with different advantages and disadvantages. I present this one because it is among the simplest.
Related
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.
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 = {}) {
# ...
}
I've created a puppet resource for interfaces. Most of the interface names on my switch are lowercase with the exception of Ethernet interfaces, so I munged the interface name to hopefully reduce errors in the manifest; e.g.:
manifest:
cisco_interface { 'Ethernet1/1': description => 'foo' }
type/cisco_interface.rb:
newparam(:name) do
munge { |value|
value.downcase
}
end
My provider code also downcases the interface names when I collect the list of interfaces with self.instances.
Okay, so this works great when I test with the manifest, but not so great with the puppet resource command which only works when I call it with the name already downcased:
switch# puppet resource cisco_interface 'Ethernet1/1'
cisco_interface { 'Ethernet1/1':
ensure => 'absent',
}
switch# puppet resource cisco_interface 'ethernet1/1'
cisco_interface { 'ethernet1/1':
ensure => 'present',
description => 'foo',
}
The puppet resource command name field seems to just be a simple filter so I think I'm stuck, but I thought I'd seen other resource types munging title values like this.
Is it possible to munge the title values in a way that works for both scenarios?
If not then I'm not sure whether it would be better to leave it case-sensitive since that is what users will see in the switch config, or to "help" them avoid errors in the manifest.
You are correct about what is happening here, puppet currently requires that the name passed into the command line exactly matches the name according to the type. It's buried a couple levels deep, but look at the find and resource_name methods of the RAL.
It doesn't seem to me like this would be a major change, so you might want to log a defect or make the change yourself!
I am going to use puppet bind module from
https://github.com/thias/puppet-bind
any idea how I can use hiera in yaml format with this?
I have tried using this in Hiera, but it does not pass the values to the module.
---
classes:
- 'bind::server'
profile::bind::conf:
'/etc/named.conf':
zones:
'example.com': ['type master', 'file ]
any suggestions?
The parameters cannot be bound to the module's classes automatically - zones are created through a define.
Creating values for define instances in Hiera is a two-step process.
Create the data. Your's is fine, but the key is misleading.
e.g.
bind_server_confs:
'/etc/named.conf':
zones:
'example.com': ['type master', 'file ]
Create resources from the hash using the create_resources function.
like
create_resources('bind::server::conf', hiera('bind_server_confs'), {})
The default result of {} will (correctly) lead to no resources being created.
I have to write a policy for defining various Host groups, for a particular thing it should check set of parameters according to host group.
For example I have 2 different set of web cluster, On one cluster httpd.conf is kept under /usr/local/apache/httpd.conf and for another set it is kept under /etc/httpd/httpd.conf.
I have a policy to check file changes of these configuration but I want a way in which I can define for a particular host group where exactly it should check.
Any Hint, help would be very appreciable.
The general answer is that you define a class for each group, and assign the appropriate path onto a variable according to that. For example:
vars:
group1::
"httpd_conf" string => "/usr/local/apache/httpd.conf";
group2::
"httpd_conf" string => "/etc/httpd/httpd.conf";
Then you use $(httpd_conf) in the file operations, and it will have the correct value according to the group.
The potentially tricker part is how to define those classes. In this case it depends on your setup and your preferences. For example, you could define the classes by explicitly listing the hosts in each group:
classes:
"group1" or => { "host1", "host2", "host3" };
"group2" or => { "host4", "host5", "host6" };
Or by matching against hostname patterns:
classes:
"group1" expression => classmatch("grp1.*");
"group2" expression => classmatch("grp2.*");
There are other possibilities. For a full treatment, please check Defining classes for groups of hosts in Chapter 6 of my book "Learning CFEngine 3".