In my previous post I walked you through configuring DHCP with hiera yaml files. These entries allow you to reserve IPs for nodes. These IPs can then also be tied to DNS entries. However I always strive to integrate all services I run. One of those core services is a Unifi Controller. Unifi’s web interface is great for viewing current Wifi clients and also to troubleshoot and track down connectivity issues. However when a system doesn’t advertise its name, you are presented with a MAC address or in the case of some IOT devices a random string. Vendor lookups don’t always help given the same chipset being used on multiple platforms. In this post I’ll show you how to sync Unifi with a yaml file.
Software | Version | OS |
---|---|---|
ruby | 2.7.0p0 | ubuntu |
Unifi | 5.6.40 | ubuntu |
I have some older Unifi hardware mixed in with newer. This older version supports both.
I run my unifi controller on my synology. I split the controller (Web interface) and its database into separate containers. This is useful for upgrades and migrating the container to different hosts and platforms over time. This also makes it easier for me to connect to the mongo api and rewrite aliases that unifi uses. Lets look at an example of blank node.
This node doesn’t add its hostname to DHCP however I know what it is when looking though my DHCP reseverations. It is the dog treat dispenser that doubles as one of the security cameras for the Synology.
furbo:
mac: '38:a2:8c:xx:xx:xx'
ip: '192.168.53.167'
The DHCP reservation is working due to the puppet code, but the synology is unable to lookup this data on its own. While its possible to import this via CSVs I want this to happen automatically do this as I add new nodes. To do this first I write a script that will read a yaml file on disk and update the names based on the format above.
Push aliases to Unifi
sync_unif_names.rb
#!/usr/bin/env ruby
require 'mongo'
require 'yaml'
begin
dhcp_hosts = YAML.load_file('/etc/dhcp.yaml')['dhcp_hosts']
puts dhcp_hosts.inspect
mac_to_name = Hash.new
dhcp_hosts.each do |k,h|
puts "Processing #{k}"
mac_to_name[h['mac'].downcase] = k
end
db = Mongo::Client.new(['synology.homeops.tech:27117'],:database => 'ace')
db.collections.each do |collection|
if collection.name == 'user'
collection.find().each do |doc|
unless doc['mac'].nil?
if mac_to_name.keys.include?(doc['mac'].downcase)
puts "Found: #{mac_to_name[doc['mac']]}"
puts doc['hostname'] unless doc['hostname'].nil?
puts doc['oui'] unless doc['oui'].nil?
collection.update_one({ _id: doc['_id'] }, { '$set' => { name: mac_to_name[doc['mac']] }})
end
end
end
end
end
rescue StandardError => err
puts('Error: ')
puts(err)
end
I can copy my dhcp.yaml
to /etc
and run the script and it will sync the names.
Automating the Sync
While its nice to be able to update the Unifi interface en masse, Ideally we should be able to this automatically as the YAML file in hiera is updated.
We can do that with an refresh
in Puppet. On the DHCP server (or any node) we apply the following code.
package {'mongo':
ensure => 'present',
provider => 'gem',
}
file {'/usr/local/bin/sync_names.rb':
ensure => 'present',
owner => 'root',
mode => '0700',
source => 'puppet:///modules/profile/sync_names.rb',
require => Package['monogo'],
}
$hosts = hiera('dhcp_hosts')
file {'/etc/dhcp.yaml':
ensure => 'present',
owner => 'root',
mode => '0700',
content => inline_template('<%= @{ dhcp_hosts: hosts }.to_yaml %>'),
notify => Exec['/usr/local/bin/sync_names.rb'],
}
exec { '/usr/local/bin/sync_names.rb':
refreshonly => true,
path => $::path,
}
Now when I add a new node to my DHCP server, the same puppet run will trigger the script to run. This happens because the local YAML file’s content will change and puppet will issue a refresh notification to the exec.
Comments