Using hiera to access facts of another node

7,172

Solution 1

So you want certain hosts to get information from another host's facts, but which host the facts come from will vary depending on the configuration of the specific host. Is that correct?

If so, I would also recommend using exported resources and using tags to specify the particular resource to use. The reason for this is that there are basically two ways for a host to get at another host's facts. Both require enabling storeconfigs. One is for the puppet master to do explicit lookups on whatever your storeconfigs backend is; I don't know of any modules that encapsulate this, so you might have to write your own. The other is for the source host to export resources that contain their facts; this is easier and the one I'll describe below.

This will be easier if you make your own resource type to wrap the firewall rules. That prevents any collisions with any other classes that happen to be exporting firewall rules.

define site_firewall ($ipaddr) {
  firewall { '500 allow site access':
    chain       => 'OUTPUT',
    destination => $ipaddr,
    proto       => 'tcp',
    port        => 10000,
  }
}

Next, each of your sites should export its own definition for site_firewall:

@@site_firewall { $hostname:
  ipaddr => $ipaddress,
}

In hiera, you define somewhere in your hierarchy the site that each host is a member of:

sitename: site1

Then in your host classes, you instantiate the appropriate site_firewall definition:

Site_firewall <<| name == hiera('sitename', 'default') |>>

A similar setup would apply for the shared hosts.

If you need firewall rules on the site and shared hosts, you should use tags rather than names because there will be multiple firewall rules for a given host. On the specific hosts:

@@firewall { "500 allow site traffic from ${hostname}":
  tag    => hiera('sitename', 'default-site'),
  source => $ipaddress,
  proto  => 'tcp',
  port   => 10000,
}
@@firewall { "500 allow shared traffic from ${hostname}":
  tag    => hiera('sharedname', 'default-shared'),
  source => $ipaddress,
  proto  => 'tcp',
  port   => 8080,
}

On the site hosts, you just need to collect the firewall rules for those hosts:

Firewall <<| tag == $hostname |>>

Edit: Aha. I think I've found the problem you were running into with exported resources. At the very least this is a gotcha that I'll document here for good measure.

If you have a resource with default parameters and you export that resource without explicitly setting those parameters, the parameter defaults are provided by the host realizing the resource, not the one exporting it.

In other words, if you have this resource type definition:

define foo ($bar = $fqdn) {
  notice($bar)
}

And you export it from host baz.example.com:

@@foo { 'title': }

And you realize it on host quux.example.com:

Foo <<| |>>

Then the value of $bar will be "quux.example.com".

If, instead you export it from baz.example.com like this:

@@foo { 'title': bar => $fqdn }

Then the value of $bar will indeed be "baz.example.com".

Solution 2

Well, I think a good option for your use case is to enable "storeconfigs" and then use "exported resources".

Here you can find some documentation about this topic including some examples:

Share:
7,172

Related videos on Youtube

Josh Smeaton
Author by

Josh Smeaton

Updated on September 18, 2022

Comments

  • Josh Smeaton
    Josh Smeaton over 1 year

    What we're trying to do is generate firewall rules (puppetlabs/firewall) for iptables. Our nodes are grouped like so conceptually:

    -- site1
    ---- shared1
    ------ specific1
    ------ specific2
    ---- shared2
    ------ specific3
    ------ specific4
    

    The node "specific4" will always need to access port 8080 on "shared2" and port 10000 on "site1". "specific1" likewise will need to access 8080 on "shared1". The rules are always going to be the same per node but they will rely on which group they are apart of.

    I'm struggling to find a way to represent this in hiera without duplication. Is it possible to get facts from a completely separate node?

    I think I want to be able to do something like this (simplified):

    --
    hosts:
      host specific4:
        rules:
          rule:
            port: 8080
            ip: get_ip(get_my_shared())
    

    But obviously, you can't call functions from a yaml file. Would the best way to go about this be using custom facts? I'm haven't actually used hiera yet - so I'm unsure of best practices and what not. Any gentle pushes in the right direction would be most appreciated.

    Edit:

    This is the solution I've gone with, but if I can use exported resources I can remove the dependency on puppetdb-query.

    # helper for creating rules from an array
    define firewall_rules($port, $service_type) {
        $source = $name
        firewall { "$port $service_type $source":
            proto       => 'tcp',
            dport       => $port,
            state       => 'NEW',
            source      => "$source",
            action      => 'accept'
        }
    }
    
    class profile::specific inherits profile {
        $site = hiera('site')
        $shared = hiera('shared')
        $query = "site=\"$site\" and shared=\"$shared\""
        $shared_hosts = query_nodes($query)
        $specific_port = hiera('specific_ports', '8080')
        firewall_rules { $shared_hosts:
            port           => $specific_port,
            service_type   => 'SPECIFIC'
        }
    }
    

    I then export the site and shared facts based on hiera data, and using puppet-stdlib to load them from file resources on the host.

    class profile::facts {
    
        $site       = hiera('site', 'none')
        $shared     = hiera('shared', 'none')
        $specific   = hiera('specific', 'none')
        $role       = hiera('role', 'none')
        $grouping   = "site=$site\nshared=$shared\nspecific=$specific\nrole=$role"
    
        notify { "facts being set: $grouping ": }
    
        file { ['/etc/facter/', '/etc/facter/facts.d/']:
            ensure  => directory,
            owner   => 'root',
            group   => 'root'
        }->
        file { '/etc/facter/facts.d/grouping.txt':
            ensure  => file,
            owner   => 'root',
            group   => 'root',
            mode    => '0775',
            content => $grouping
        }
    }
    

    As I said, this works, but I'd prefer to use exported resources if possible. The problem I ran into was that the resource doing the exporting couldn't also export its own IP/Host for collection. Perhaps I missed something, but I don't think it's possible, as the exporting happens when the resource is parsed, not when the node that contains that resource is realized.

  • Josh Smeaton
    Josh Smeaton about 11 years
    That definitely could be an approach I could take - thank you. I'll invest some more time reading about storeconfigs. I need a better way of grouping these nodes into their various related roles, such that the same roles in different groups operate differently based on their related services.
  • Josh Smeaton
    Josh Smeaton about 11 years
    Unfortunately this won't work for me. Exported resources are realized with the facts on the node collecting the resource. I need a way of exporting the host/ip of the implementing node of the export, and using the port from the node collecting the resource. I'm looking into puppetdb-query for getting facts out of related nodes.
  • Josh Smeaton
    Josh Smeaton about 11 years
    Thanks for the detailed write up. I'm currently doing it with puppetdb-query and using the master to query the required facts, using parameters retrieved from hiera. The problem I had with your solution was that the ipaddress was realized on the host doing collection, not the host doing the exporting. Meaning the collector was opening up its port with its own IP as the source. Does your solution (or can it be made to) export its hostname/ipaddress using its own facts along with the resource? I'll update my question with the solution I've gone with.
  • Josh Smeaton
    Josh Smeaton about 11 years
    Maybe I messed up somewhere else and took it that the collector was using it's own facts rather than the exporter - which contradicts everything the docs say with the nagios examples. I may also have confused myself since I need the exporters IP, but the collector to define which port will be open. I'll revisit this tomorrow at work. Thanks for the update.
  • Josh Smeaton
    Josh Smeaton about 11 years
    This all worked perfectly. I did need a way of defining on the exporter which port on the collector should be opened, but we are using standard ports anyway, and retrieving from hiera incase an override is required in certain cases. Thanks for the help.
  • asciiphil
    asciiphil about 11 years
    @JoshSmeaton I think I may have figured out why you were seeing facts being realized on the collecting host; take a look at the edit to my answer.