j7nn7k
j7nn7k

Reputation: 18582

Dynamic Role Attributes in Chef

I would like the Chef cookbook network_interfaces to have dynamic values for ip addresses, netmasks and alike for each of my nodes. What works for me is the following:

db_role.rb (block1):

override_attributes(
  "network_interfaces" => {
      :device => 'eth0',
      :address => '123.123.123.123',
  }
)

But that is not very dynamic. My idea was to submit the ip address(, netmask, etc.) to each node on knife bootstrap.

The node would then look like so (block2):

{
    "normal": {
      "network_interfaces" => {
          "device" : "eth0",
          "address" : "123.123.123.123"
      }
    },
    "name": "foobar",
    "run_list": [
       "recipe[zsh]",
       "role[networking_interfaces]"
    ]
}

Unfortunately the network_interfaces cookbook does not pick up those values by default. My idea was to reference the node specific attributes shown in block2 in the roles definition like so:

override_attributes(
  "network_interfaces" => {
      :device => node['network_interfaces']['device'],
      :address => node['network_interfaces']['address'],
  }
)

This does not work because it is not json obviously and Chef can not handle dynamically allocated values in roles files.

How can I achieve to run the network_interfaces recipe and pass my node specific values to it?

Upvotes: 4

Views: 3281

Answers (2)

j7nn7k
j7nn7k

Reputation: 18582

Maciej, I followed your suggestions. I upload the custom parameters like IP, broadcast, etc with the -j option upon bootstrap.

knife bootstrap IP_OF_THE_NODE -r 'role[main_application]' -j '{ "network_interfaces" : {"device" : "eth1.0000", "type" : "static", "address" : "192.168.1.1", "netmask" : "255.255.255.0", "broadcast" : "192.168.0.255","gateway": "192.168.0.1"}}'     

Plus I wrote a custom recipe to achieve the dynamic matching. Here's the code:

#setup a custom network config per vm
network_interfaces node["network_interfaces"]["device"] do
  Chef::Log.info("Compiling network_interfaces")
  target node["network_interfaces"]["address"]
  mask node["network_interfaces"]["netmask"]
  broadcast node["network_interfaces"]["broadcast"]
  gateway node["network_interfaces"]["gateway"]
  custom node["network_interfaces"]["custom"]
  action :save
end

Upvotes: 6

Maciej Pasternacki
Maciej Pasternacki

Reputation: 2880

If you add normal attributes via knife bootstrap -j …, and leave override attributes in the role, the override will take over (see http://docs.opscode.com/essentials_node_object_attributes_precedence.html for a complete list of attribute precedence). If you have deleted override_attributes from db_role.rb before running knife bootstrap, or changed it to default_attributes, then setting IP in node attributes should have worked.

The last snippet won't work: roles are static JSON documents on the Chef server, and Ruby is only interpreted once by knife when uploading role to the server (http://docs.opscode.com/essentials_roles_formats.html). You can't refer to node's attributes from role's Ruby code, as it's compiled to JSON before it even touches any node. If you want to try a similar approach, you need to use a custom cookbook (say, my_network_interfaces) with a recipe that would look somewhat like this:

node.set['network_interface']['device'] = …
node.set['network_interface']['address'] = …
include_recipe 'network_interfaces'

This way, you'd use network_interfaces as a "library" cookbook, called by your "application" cookbook my_network_interfaces which implements whatever logic you need. From your question, I can't suggest how would you compute device and address, as your example just tries to copy same attributes, which is a no-op. As far as I understand what you want to achieve, you want to have default_attributes in the role, and pass specific JSON with normal attributes to knife bootstrap to override the defaults.

Upvotes: 2

Related Questions