Skip to content

Commit

Permalink
generate_mac function
Browse files Browse the repository at this point in the history
generates mac addresses for virtual machines
  • Loading branch information
rrotter committed Jan 21, 2025
1 parent 9e06675 commit 4368df1
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
38 changes: 38 additions & 0 deletions lib/puppet/functions/generate_mac.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require 'digest'

# generates a deterministic pseudorandom mac address, using hostname as seed
#
# prefix validated to follow the following parameters:
# - must contain exactly 6 hex digits
# - second digit must be one of [2,6,a,e] (per RFC7042 §2.1, see "Local bit")
# - case is ignored
# - may contain unlimited number of non-alphanumerics as delimeters
#
# generated address contains
# - 3 byte fixed OUI (prefix)
# - 21 "random" bits (seeded from hostname)
# - 3 bit sequential number (index)
Puppet::Functions.create_function(:generate_mac) do
dispatch :generate_mac do
param "String", :prefix
param "String", :hostname
param "Integer", :index
end

def generate_mac(prefix, hostname, index)
unless(index.between?(0,7))
raise(ArgumentError, "#{index} must be between 0 and 7, I can only generate 8 mac addresses per host!")
end

oui = prefix.downcase.gsub(/[^0-9a-z]/,'')
unless(oui =~ /^[a-f0-9][26ae][a-f0-9]{4}$/)
raise(ArgumentError, "invalid mac prefix!")
end

integer_id = (Digest::SHA256.hexdigest(hostname)[0,6].to_i(16) & 0xFF_FF_F8) + index
hex_id = sprintf('%06x',integer_id)
"#{oui}#{hex_id}".each_char.each_slice(2).map{|x| x.join}.join(':')
end
end
29 changes: 29 additions & 0 deletions spec/functions/generate_mac_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

# Copyright (c) 2020 The Regents of the University of Michigan.
# All Rights Reserved. Licensed according to the terms of the Revised
# BSD License. See LICENSE.txt for details.
require "spec_helper"

describe "generate_mac" do
it "generates simple addresses" do
is_expected.to run.with_params('02:00:00', 'example.com', 0).and_return('02:00:00:a3:79:a0')
is_expected.to run.with_params('12-0f-00', 'example.com', 2).and_return('12:0f:00:a3:79:a2')
end
it "fails on invalid chars" do
is_expected.to run.with_params('g2:00:00', 'example.com', 0).and_raise_error(ArgumentError)
end
it "correctly masks final 3 bits" do
is_expected.to run.with_params('06 12 34', 'my.host.name', 0).and_return('06:12:34:3b:d9:98')
is_expected.to run.with_params('5a.67.89', 'my.host.name', 3).and_return('5a:67:89:3b:d9:9b')
is_expected.to run.with_params('AE:BC:DE', 'my.host.name', 7).and_return('ae:bc:de:3b:d9:9f')
end
it "fails on non-private oui" do
is_expected.to run.with_params('03:00:00', 'example.com', 0).and_raise_error(ArgumentError)
end
it "fails on out of range index" do
is_expected.to run.with_params('02:00:00', 'example.com', 11).and_raise_error(ArgumentError)
is_expected.to run.with_params('02:00:00', 'example.com', -1).and_raise_error(ArgumentError)
end
end

0 comments on commit 4368df1

Please sign in to comment.