summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom May <thom@chef.io>2018-05-30 15:15:20 +0100
committerThom May <thom@chef.io>2018-05-30 15:15:20 +0100
commitb0ed912e9b46f67c9e76514ed0fdbfcc87885df7 (patch)
treedf1aa68a9ca15cebefa6fcfa8a21f5bd8b3358ae
parent2d687d12053e2888efa0441fa72f32faf6c7c5f7 (diff)
downloadchef-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.rb1
-rw-r--r--lib/chef/provider/ifconfig/systemd.rb174
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/ifconfig.rb4
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