Add NixOS guest support

This commit is contained in:
Charles Strahan 2014-05-14 22:22:48 -04:00
parent 9475ed9a50
commit 38d749dc1f
6 changed files with 228 additions and 0 deletions

View File

@ -0,0 +1,41 @@
require 'tempfile'
require "vagrant/util/template_renderer"
module VagrantPlugins
module GuestNixos
module Cap
class ChangeHostName
include Vagrant::Util
def self.change_host_name(machine, name)
# upload the config file
hostname_module = TemplateRenderer.render("guests/nixos/hostname", :name => name)
upload(machine, hostname_module, "/etc/nixos/vagrant-hostname.nix")
end
# Upload a file.
def self.upload(machine, content, remote_path)
local_temp = Tempfile.new("vagrant-upload")
local_temp.binmode
local_temp.write(content)
local_temp.close
remote_temp = mktemp(machine)
machine.communicate.upload(local_temp.path, "#{remote_temp}")
local_temp.delete
machine.communicate.sudo("mv #{remote_temp} #{remote_path}")
end
# Create a temp file.
def self.mktemp(machine)
path = nil
machine.communicate.execute("mktemp --suffix -vagrant-upload") do |type, result|
path = result.chomp if type == :stdout
end
path
end
end
end
end
end

View File

@ -0,0 +1,129 @@
require 'tempfile'
require 'ipaddr'
require "vagrant/util/template_renderer"
module VagrantPlugins
module GuestNixos
module Cap
class ConfigureNetworks
include Vagrant::Util
def self.configure_networks(machine, networks)
# set the prefix length.
networks.each do |network|
network[:prefix_length] = (network[:netmask] && netmask_to_cidr(network[:netmask]))
end
# set the device names.
assign_device_names(machine, networks)
# upload the config file
network_module = TemplateRenderer.render("guests/nixos/network", :networks => networks)
upload(machine, network_module, "/etc/nixos/vagrant-network.nix")
end
# Set :device on each network.
# Attempts to use biosdevname when available to detect interface names,
# and falls back to ifconfig otherwise.
def self.assign_device_names(machine, networks)
if machine.communicate.test("command -v biosdevname")
# use biosdevname to get info about the interfaces
interfaces = get_interfaces(machine)
if machine.provider.capability?(:nic_mac_addresses)
# find device name by MAC lookup.
mac_addresses = machine.provider.capability(:nic_mac_addresses)
networks.each do |network|
mac_address = mac_addresses[network[:interface]+1]
interface = interfaces.detect {|nic| nic[:mac_address].gsub(":","") == mac_address} if mac_address
network[:device] = interface[:kernel] if interface
end
else
# assume interface numbers correspond to (ethN+1).
networks.each do |network|
interface = interfaces.detect {|nic| nic[:ethn] == network[:interface]}
network[:device] = interface[:kernel] if interface
end
end
else
# assume interface numbers correspond to the order of interfaces.
interfaces = get_interface_names(machine)
networks.each do |network|
network[:device] = interfaces[network[:interface]]
end
end
end
def self.get_interface_names(machine)
output = nil
machine.communicate.execute("ifconfig -a") do |type, result|
output = result.chomp if type == :stdout
end
names = output.scan(/^[^:\s]+/).reject {|name| name =~ /^lo/ }
names
end
# Upload a file.
def self.upload(machine, content, remote_path)
local_temp = Tempfile.new("vagrant-upload")
local_temp.binmode
local_temp.write(content)
local_temp.close
remote_temp = mktemp(machine)
machine.communicate.upload(local_temp.path, "#{remote_temp}")
local_temp.delete
machine.communicate.sudo("mv #{remote_temp} #{remote_path}")
end
# Create a temp file.
def self.mktemp(machine)
path = nil
machine.communicate.execute("mktemp --suffix -vagrant-upload") do |type, result|
path = result.chomp if type == :stdout
end
path
end
# using biosdevname, get all interfaces as a list of hashes, where:
# :kernel - the kernel's name for the device,
# :ethn - the calculated ethN-style name converted to integer.
# :mac_address - the permanent mac address. ethN-style name converted to integer.
def self.get_interfaces(machine)
interfaces = []
# get all adapters, as named by the kernel
output = nil
machine.communicate.sudo("biosdevname -d") do |type, result|
output = result if type == :stdout
end
kernel_if_names = output.scan(/Kernel name: ([^\n]+)/).flatten
mac_addresses = output.scan(/Permanent MAC: ([^\n]+)/).flatten
# get ethN-style names
ethns = []
kernel_if_names.each do |name|
machine.communicate.sudo("biosdevname --policy=all_ethN -i #{name}") do |type, result|
ethns << result.gsub(/[^\d]/,'').to_i if type == :stdout
end
end
# populate the interface list
kernel_if_names.each_index do |i|
interfaces << {
:kernel => kernel_if_names[i],
:ethn => ethns[i],
:mac_address => mac_addresses[i]
}
end
interfaces
end
def self.netmask_to_cidr(mask)
IPAddr.new(mask).to_i.to_s(2).count("1")
end
end
end
end
end

View File

@ -0,0 +1,11 @@
require "vagrant"
module VagrantPlugins
module GuestNixos
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("test -f /run/current-system/nixos-version")
end
end
end
end

View File

@ -0,0 +1,25 @@
require "vagrant"
module VagrantPlugins
module GuestNixos
class Plugin < Vagrant.plugin("2")
name "NixOS guest"
description "NixOS guest support."
guest("nixos", "linux") do
require File.expand_path("../guest", __FILE__)
Guest
end
guest_capability("nixos", "configure_networks") do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("nixos", "change_host_name") do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
end
end
end

View File

@ -0,0 +1,6 @@
{ config, pkgs, ... }:
{
<% if name %>
networking.hostName = "<%= name %>";
<% end %>
}

View File

@ -0,0 +1,16 @@
{ config, pkgs, ... }:
{
networking.interfaces = [
<% networks.select {|n| n[:device]}.each do |network| %>
{
name = "<%= network[:device] %>";
<% if network[:type] == :static %>
ipAddress = "<%= network[:ip] %>";
<% end %>
<% if network[:prefix_length] %>
prefixLength = <%= network[:prefix_length] %>;
<% end %>
}
<% end %>
];
}