0

I've wrote some ruby code which will run on a linux server and return details about the server as a fact. It does this by connecting to amazon and retrieving some json (it runs two separate commands one to retrieve a list of disks - e.g /dev/sda1, /dev/xvdb and then it maps this to a volumeID via another query).

I've made some small amendments to the output and added some values I'm interested in. The code runs multiple times and returns multiple hashes (one for each disk - maybe I should merge them?). Anyway here's an example of a server which has two disks below (this is just some debug output):

Hash is {"/dev/sda1"=>{"disk_mapping"=>"", "is_lvm_partitioned"=>"false", "volumeid"=>"vol1234"}}.
Hash is {"/dev/xvdb1"=>{"disk_mapping"=>"xvdb1", "is_lvm_partitioned"=>"true", "volumeid"=>"vol5678"}}.

The next thing I want to to is turn this into a structured fact (with the devices: /dev/sda1, /dev/xvdb1 as the "keys"). Here's a rough idea of how I've done it (I've skipped a lot of the irrelevant code).

json_string = {
  "#{path}" => {
  "disk_mapping"       => "#{disk_mapping}",
  "is_lvm_partitioned" => "#{is_lvm_partitioned}",
  "volumeid"           => "#{getVolumes}"
  }
}.to_json
hash = JSON.parse(json_string)
if hash.is_a? Hash then
  debug_msg("Hash is #{hash}.")
  hash["#{path}"].each do |key, child|
    debug_msg("Setting key: #{key} child: #{child}.")
  end
end

I've never really wrote any ruby before so this is copied from multiple places, but besides aggregated facts I can't find a way to do this; I've tried to do something like this:

Facter.add(:test["#{path}"]["#{key}"]) do
  setcode do
    #{child}
  end
end

So I guess in order I want to know:

  1. Should I merge the hash somehow? I originally assumed I did but found this incredibly hard due to not knowing how many hash's I'd have.
  2. Should I be using an aggregated fact or a "standard" one?
  3. How do I retain the structure of the hash and then call it with a single query (e.g facts test).
  4. Any examples which are similar to my code (the puppet ones I've found quite hard to follow).

Here's what I'm looking for at the end:

[root@blah ~]# facter test
{
  /dev/sda1 => {
    disk_mapping => "",
    is_lvm_partitioned => "false",
    volumeid => "vol1234"
  },
  /dev/xvdb => {
    disk_mapping => "xvdb1",
    is_lvm_partitioned => "true",
    volumeid => "vol5678"
  }
}

Thanks.

keeer
  • 783
  • 1
  • 5
  • 11
  • 1
    I'm not quite following what the actual question for us is. But as for `Facter.add()` *vs* `Facter.value()`, the former defines (but does not evaluate) a new fact, whereas the latter computes and returns the value of a fact that Facter already knows about. – John Bollinger May 07 '22 at 00:17
  • So if I want to create a new structured fact (example) I use Facter.add(:example) and then under that do I put in the whole hash as the value? Will that retain the hash "hierachy"? I'll add some more specific notes so this is clearer for people. – keeer May 07 '22 at 06:25
  • 1
    When Facter attempts to determine the value of your fact, it will (try to) choose one of the `setcode` blocks you have defined via `Facter.add()`, subject to any constraints and priority you have set. This is "resolution". It will then execute that block, and the resulting value is the value of the fact. Arrayness and hashness of that value and its members, recursively, are preserved. – John Bollinger May 07 '22 at 14:51
  • Ok, that simplifies my code at a lot. Two more questions. If the same fact is declared twice in the same code with different values, I assume the original result would be overwritten (thus I need to merge my hash)? Also to retrieve the fact, do I need some puppet code to run it (simply running facter seems to return an empty result which is partly why I think I had problems in the first place). Thanks a lot for all your help, it's very much appreciated. – keeer May 07 '22 at 16:02
  • This seems to be working, I think I just need to merge my hashes. I've also noticed that all the json crap isn't necessary. – keeer May 07 '22 at 18:02
  • 1
    I've got it working, woohoo! Thanks for your help :D – keeer May 07 '22 at 18:24

1 Answers1

0

I was way off...

hash_template = {}
hash = hash_template.merge!({
  "#{path}" => {
  "disk_mapping"       => "#{disk_mapping}",
  "is_lvm_partitioned" => "#{is_lvm_partitioned}",
  "volumeid"           => "#{getVolumes}"
  }
}
if hash.is_a? Hash then
  Facter.add(:test) do
    setcode do
      begin
        "#{hash}"
      rescue
        nil
      end
    end
  end
end

This can only be retrieved/viewed by calling it (within as puppet manifest):

if $test {
  notify { "Test fact is set as $test": }
}
keeer
  • 783
  • 1
  • 5
  • 11