diff --git a/lib/resources/interface.rb b/lib/resources/interface.rb new file mode 100644 index 000000000..f65f97211 --- /dev/null +++ b/lib/resources/interface.rb @@ -0,0 +1,122 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter + +# Usage: +# describe interface('eth0') do +# it { should exist } +# it { should be_up } +# its(:speed) { should eq 1000 } +# end + +require 'utils/convert' + +class NetworkInterface < Vulcano.resource(1) + name 'interface' + + def initialize(iface) + @iface = iface + + @interface_provider = nil + if vulcano.os.linux? + @interface_provider = LinuxInterface.new(vulcano) + elsif vulcano.os.windows? + @interface_provider = WindowsInterface.new(vulcano) + else + return skip_resource 'The `interface` resource is not supported on your OS yet.' + end + end + + def exists? + !interface_info.nil? && !interface_info[:name].nil? + end + + def up? + interface_info.nil? ? false : interface_info[:up] + end + + # returns link speed in Mbits/sec + def speed + interface_info.nil? ? nil : interface_info[:speed] + end + + private + + def interface_info + return @cache if defined?(@cache) + @cache = @interface_provider.interface_info(@iface) if !@interface_provider.nil? + end +end + +class InterfaceInfo + include Converter + def initialize(vulcano) + @vulcano = vulcano + end +end + +class LinuxInterface < InterfaceInfo + def interface_info(iface) + # will return "[mtu]\n1500\n[type]\n1" + cmd = @vulcano.command("find /sys/class/net/#{iface}/ -type f -maxdepth 1 -exec sh -c 'echo \"[$(basename {})]\"; cat {} || echo -n' \\;") + return nil if cmd.exit_status.to_i != 0 + + # parse values, we only recieve values, therefore we threat them as keys + params = SimpleConfig.new(cmd.stdout.chomp).params + + # abort if we got an empty result-set + return nil if params.empty? + + # parse state + state = false + if params.key?('operstate') + operstate, _value = params['operstate'].first + state = operstate == 'up' + end + + # parse speed + speed = nil + if params.key?('speed') + speed, _value = params['speed'].first + speed = convert_to_i(speed) + end + + { + name: iface, + up: state, + speed: speed, + } + end +end + +class WindowsInterface < InterfaceInfo + def interface_info(iface) + # gather all network interfaces + cmd = @vulcano.command('Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json') + + # filter network interface + begin + net_adapter = JSON.parse(cmd.stdout) + rescue JSON::ParserError => _e + return nil + end + + # ensure we have an array of groups + net_adapter = [net_adapter] if !net_adapter.is_a?(Array) + + # select the requested interface + adapters = net_adapter.each_with_object([]) do |adapter, adapter_collection| + # map object + info = { + name: adapter['Name'], + up: adapter['State'] == 2, + speed: adapter['ReceiveLinkSpeed'] / 1000, + } + adapter_collection.push(info) if info[:name].casecmp(iface) == 0 + end + + return nil if adapters.size == 0 + warn "[Possible Error] detected multiple network interfaces with the name #{iface}" if adapters.size > 1 + adapters[0] + end +end diff --git a/lib/vulcano/resource.rb b/lib/vulcano/resource.rb index 9be6a6363..f91c8cf43 100644 --- a/lib/vulcano/resource.rb +++ b/lib/vulcano/resource.rb @@ -34,6 +34,7 @@ require 'resources/gem' require 'resources/group' require 'resources/group_policy' require 'resources/inetd_conf' +require 'resources/interface' require 'resources/json' require 'resources/kernel_module' require 'resources/kernel_parameter' diff --git a/test/helper.rb b/test/helper.rb index 2e169b2e2..847818330 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -79,6 +79,11 @@ class MockLoader stdout = ::File.read(::File.join(scriptpath, '/unit/mock/cmd/'+x)) mock.mock_command(stdout, '', 0) } + + empty = lambda { + mock.mock_command('', '', 0) + } + mock.commands = { 'ps aux' => cmd.call('ps-aux'), 'type win_secpol.cfg' => cmd.call('secedit-export'), @@ -124,12 +129,12 @@ class MockLoader 'service sendmail onestatus' => cmd.call('service-sendmail-onestatus'), # services for system 5 e.g. centos6, debian 6 'service sshd status' => cmd.call('service-sshd-status'), - 'find /etc/rc*.d -name S*' => cmd.call('find-etc-rc-d-name-S'), + 'find /etc/rc*.d -name S*' => cmd.call('find-etc-rc-d-name-S'), 'ls -1 /etc/init.d/' => cmd.call('ls-1-etc-init.d'), # user information for linux 'id root' => cmd.call('id-root'), 'getent passwd root' => cmd.call('getent-passwd-root'), - 'chage -l root' => cmd.call('chage-l-root'), + 'chage -l root' => cmd.call('chage-l-root'), # user info for mac 'id chartmann' => cmd.call('id-chartmann'), 'dscl -q . -read /Users/chartmann NFSHomeDirectory PrimaryGroupID RecordName UniqueID UserShell' => cmd.call('dscl'), @@ -138,7 +143,11 @@ class MockLoader # user info for windows '650b6b72a66316418b25421a54afe21a230704558082914c54711904bb10e370' => cmd.call('GetUserAccount'), # group info for windows - 'Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json' => cmd.call('GetWin32Group'), + 'Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json' => cmd.call('GetWin32Group'), + # network interface + '9e80f048a1af5a0f6ab8a465e46ea5ed5ba6587e9b5e54a7a0c0a1a02bb6f663' => cmd.call('find-net-interface'), + 'c33821dece09c8b334e03a5bb9daefdf622007f73af4932605e758506584ec3f' => empty.call, + 'Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json' => cmd.call('Get-NetAdapter'), } # set os emulation diff --git a/test/unit/mock/cmd/Get-NetAdapter b/test/unit/mock/cmd/Get-NetAdapter new file mode 100644 index 000000000..07aa11f77 --- /dev/null +++ b/test/unit/mock/cmd/Get-NetAdapter @@ -0,0 +1,24 @@ +[ + { + "Name": "vEthernet (Intel(R) PRO 1000 MT Network Connection - Virtual Switch)", + "InterfaceDescription": "Hyper-V Virtual Ethernet Adapter #2", + "Status": "Up", + "State": 2, + "MacAddress": "00-0C-29-E3-48-9B", + "LinkSpeed": "10 Gbps", + "ReceiveLinkSpeed": 10000000000, + "TransmitLinkSpeed": 10000000000, + "Virtual": true + }, + { + "Name": "Ethernet0", + "InterfaceDescription": "Intel(R) PRO/1000 MT Network Connection", + "Status": "Not Present", + "State": 3, + "MacAddress": "00-0C-29-E3-48-9B", + "LinkSpeed": "0 bps", + "ReceiveLinkSpeed": 0, + "TransmitLinkSpeed": 0, + "Virtual": false + } +] diff --git a/test/unit/mock/cmd/find-net-interface b/test/unit/mock/cmd/find-net-interface new file mode 100644 index 000000000..e9482e757 --- /dev/null +++ b/test/unit/mock/cmd/find-net-interface @@ -0,0 +1,9 @@ +[speed] +10000 +[phys_switch_id] +[duplex] +full +[address] +02:42:ac:11:00:11 +[operstate] +up diff --git a/test/unit/resource_interface_test.rb b/test/unit/resource_interface_test.rb new file mode 100644 index 000000000..cd70f6cec --- /dev/null +++ b/test/unit/resource_interface_test.rb @@ -0,0 +1,54 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter + +require 'helper' +require 'vulcano/resource' + +describe 'Vulcano::Resources::Group' do + + # ubuntu 14.04 + it 'verify interface on ubuntu' do + resource = MockLoader.new(:ubuntu1404).load_resource('interface', 'eth0') + _(resource.exists?).must_equal true + _(resource.up?).must_equal true + _(resource.speed).must_equal 10000 + end + + it 'verify invalid interface on ubuntu' do + resource = MockLoader.new(:ubuntu1404).load_resource('interface', 'eth1') + _(resource.exists?).must_equal false + _(resource.up?).must_equal false + _(resource.speed).must_equal nil + end + + it 'verify interface on windows' do + resource = MockLoader.new(:windows).load_resource('interface', 'ethernet0') + _(resource.exists?).must_equal true + _(resource.up?).must_equal false + _(resource.speed).must_equal 0 + end + + it 'verify interface on windows' do + resource = MockLoader.new(:windows).load_resource('interface', 'vEthernet (Intel(R) PRO 1000 MT Network Connection - Virtual Switch)') + _(resource.exists?).must_equal true + _(resource.up?).must_equal true + _(resource.speed).must_equal 10000000 + end + + it 'verify invalid interface on windows' do + resource = MockLoader.new(:windows).load_resource('interface', 'eth1') + _(resource.exists?).must_equal false + _(resource.up?).must_equal false + _(resource.speed).must_equal nil + end + + # undefined + it 'verify interface on unsupported os' do + resource = MockLoader.new(:undefined).load_resource('interface', 'eth0') + _(resource.exists?).must_equal false + _(resource.up?).must_equal false + _(resource.speed).must_equal nil + end + +end