diff options
author | Thom May <thom@chef.io> | 2018-05-30 15:15:20 +0100 |
---|---|---|
committer | Thom May <thom@chef.io> | 2018-05-30 15:15:20 +0100 |
commit | b0ed912e9b46f67c9e76514ed0fdbfcc87885df7 (patch) | |
tree | df1aa68a9ca15cebefa6fcfa8a21f5bd8b3358ae | |
parent | 2d687d12053e2888efa0441fa72f32faf6c7c5f7 (diff) | |
download | chef-tm/systemd_networkd.tar.gz |
Experimental systemd provider for ifconfigtm/systemd_networkd
Signed-off-by: Thom May <thom@chef.io>
-rw-r--r-- | lib/chef/provider/ifconfig/debian.rb | 1 | ||||
-rw-r--r-- | lib/chef/provider/ifconfig/systemd.rb | 174 | ||||
-rw-r--r-- | lib/chef/providers.rb | 1 | ||||
-rw-r--r-- | lib/chef/resource/ifconfig.rb | 4 |
4 files changed, 177 insertions, 3 deletions
diff --git a/lib/chef/provider/ifconfig/debian.rb b/lib/chef/provider/ifconfig/debian.rb index aee3ca02dc..9ccf51993d 100644 --- a/lib/chef/provider/ifconfig/debian.rb +++ b/lib/chef/provider/ifconfig/debian.rb @@ -62,7 +62,6 @@ iface <%= new_resource.device %> <%= new_resource.family %> static protected def enforce_interfaces_dot_d_sanity - # on ubuntu 18.04 there's no interfaces file and it uses interfaces.d by default return if ::File.directory?(INTERFACES_DOT_D_DIR) && !::File.exist?(INTERFACES_FILE) # create /etc/network/interfaces.d via dir resource (to get reporting, etc) dir = Chef::Resource::Directory.new(INTERFACES_DOT_D_DIR, run_context) diff --git a/lib/chef/provider/ifconfig/systemd.rb b/lib/chef/provider/ifconfig/systemd.rb new file mode 100644 index 0000000000..9341e72342 --- /dev/null +++ b/lib/chef/provider/ifconfig/systemd.rb @@ -0,0 +1,174 @@ +# +# Copyright:: Copyright 2018, Chef Software, Inc +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require "ipaddr" +require "iniparse" + +require "chef/mixin/which" +require "chef/mixin/shell_out" + +class Chef + class Provider + class Ifconfig + class Systemd < Chef::Provider::Ifconfig + extend Chef::Mixin::Which + include Chef::Mixin::ShellOut + extend Chef::Mixin::ShellOut + + provides :ifconfig do + systemd_unit_enabled?("systemd-networkd") + end + + def load_current_resource + @current_resource = Chef::Resource::Ifconfig.new(new_resource.name) + + @status = shell_out_with_systems_locale!("ip", "-json", "addr", "list") + json = JSON.parse @status.stdout + interfaces = json.each_with_object({}) { |iface, acc| acc[iface["ifname"]] = iface } + + iface = driver_name + interface = interfaces.fetch(iface, nil) + if !interface.nil? + current_resource.target(new_resource.target) + current_resource.device(new_resource.device) + current_resource.hwaddr(interface["address"]) + current_resource.mtu(interface["mtu"]) + address = interface["addr_info"].select { |i| i["label"] == new_resource.device && i["family"] == new_resource.family }.first + return current_resource if address.nil? + netmask = get_netmask(address["prefixlen"]) + current_resource.inet_addr(address["local"]) + current_resource.bcast(address["broadcast"]) + current_resource.mask(netmask) + end + current_resource + end + + def action_add + unit = generate_network_unit + converge_by "Creating network unit: #{unit_name}" do + declare_resource(:directory, unit_dir) do + mode "0755" + owner "root" + group "root" + end + + declare_resource(:file, ::File.join(unit_dir, unit_name)) do + content unit + owner "root" + group "root" + mode "0644" + # It appears that systemd-analyze doesn't currently support network units + # verify :systemd_unit + end + end + end + + def self.systemd_unit_enabled?(unit) + systemctl_execute!("is-enabled", unit).exitstatus == 0 + end + + def self.systemctl_execute!(action, unit) + systemctl_path = which("systemctl") + systemctl_cmd = "#{systemctl_path} --system" + shell_out_with_systems_locale!("#{systemctl_cmd} #{action} #{Shellwords.escape(unit)}") + end + + private + + def unit_dir + "/etc/systemd/network" + end + + def generate_network_unit + unit = { + "Match" => { + "Name" => driver_name, + }, + "Address" => { + "Label" => new_resource.device, + }, + } + if new_resource.bootproto == "dhcp" + unit["Network"] ||= {} + unit["Network"]["DHCP"] = "YES" + return unit + end + unit["Address"]["Address"] = target + if new_resource.hwaddr + unit["Link"] ||= {} + unit["Link"]["MACAddress"] = new_resource.hwaddr + end + if new_resource.mtu + unit["Link"] ||= {} + unit["Link"]["MTUBytes"] = new_resource.mtu + end + to_systemd_unit(unit) + end + + def to_systemd_unit(content) + case content + when Hash + IniParse.gen do |doc| + content.each_pair do |sect, opts| + doc.section(sect) do |section| + opts.each_pair do |opt, val| + section.option(opt, val) + end + end + end + end.to_s + else + content.to_s + end + end + + def target + prefix = mask_to_prefix(new_resource.mask) + "#{new_resource.target}/#{prefix}" + end + + def safe_device_name + new_resource.device.tr(":", "_") + end + + def unit_name + "01-chef-#{driver_name}.network" + end + + def mask_to_prefix(mask) + return 32 if mask.nil? + mask_int = IPAddr.new(mask).to_i + n = IPAddr::IN4MASK ^ mask_int + i = 32 + while n > 0 + n >>= 1 + i -= 1 + end + i + end + + def prefix_to_mask(prefix) + mask_int = ((IPAddr::IN4MASK >> prefix) << prefix) + (0..3).map { |i| (mask_int >> (24 - 8 * i)) & 0xff }.join(".") + end + + def driver_name + new_resource.device.split(":")[0] + end + end + end + end +end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index cd265b0618..e9264eada5 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -141,6 +141,7 @@ require "chef/provider/remote_file/content" require "chef/provider/cookbook_file/content" require "chef/provider/template/content" +require "chef/provider/ifconfig/systemd" require "chef/provider/ifconfig/redhat" require "chef/provider/ifconfig/debian" require "chef/provider/ifconfig/aix" diff --git a/lib/chef/resource/ifconfig.rb b/lib/chef/resource/ifconfig.rb index 579a4eeedb..0c347dd78f 100644 --- a/lib/chef/resource/ifconfig.rb +++ b/lib/chef/resource/ifconfig.rb @@ -41,8 +41,8 @@ class Chef property :family, String, default: "inet" property :inet_addr, String property :bcast, String - property :mtu, String - property :metric, String + property :mtu, [String, Integer] + property :metric, [String, Integer] property :device, String, identity: true property :onboot, String property :network, String |