ohai 7.0.4 → 7.2.0.alpha.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,7 +19,8 @@ Ohai.plugin(:Passwd) do
19
19
  Etc.passwd do |entry|
20
20
  user_passwd_entry = Mash.new(:dir => entry.dir, :gid => entry.gid, :uid => entry.uid, :shell => entry.shell, :gecos => entry.gecos)
21
21
  user_passwd_entry.each_value {|v| fix_encoding(v)}
22
- etc[:passwd][fix_encoding(entry.name)] = user_passwd_entry
22
+ entry_name = fix_encoding(entry.name)
23
+ etc[:passwd][entry_name] = user_passwd_entry unless etc[:passwd].has_key?(entry_name)
23
24
  end
24
25
 
25
26
  Etc.group do |entry|
@@ -50,7 +50,7 @@ Ohai.plugin(:Platform) do
50
50
  when /^\s*(OpenSolaris).*snv_(\d+).*$/
51
51
  platform "opensolaris"
52
52
  platform_version $2
53
- when /^\s*(Oracle Solaris) (\d+)\s.*$/
53
+ when /^\s*(Oracle Solaris)/
54
54
  platform "solaris2"
55
55
  when /^\s*(Solaris)\s.*$/
56
56
  platform "solaris2"
@@ -94,9 +94,9 @@ Ohai.plugin(:Uptime) do
94
94
  end
95
95
 
96
96
  collect_data(:windows) do
97
- require 'ruby-wmi'
98
-
99
- uptime_seconds ::WMI::Win32_PerfFormattedData_PerfOS_System.find(:first).SystemUpTime.to_i
97
+ require 'wmi-lite/wmi'
98
+ wmi = WmiLite::Wmi.new
99
+ uptime_seconds wmi.first_of('Win32_PerfFormattedData_PerfOS_System')['systemuptime'].to_i
100
100
  uptime seconds_to_human(uptime_seconds)
101
101
  end
102
102
 
@@ -20,13 +20,16 @@ Ohai.plugin(:CPU) do
20
20
  provides "cpu"
21
21
 
22
22
  collect_data(:windows) do
23
- require 'ruby-wmi'
23
+ require 'wmi-lite/wmi'
24
24
 
25
25
  cpuinfo = Mash.new
26
26
  cpu_number = 0
27
27
  index = 0
28
28
 
29
- WMI::Win32_Processor.find(:all).each do |processor|
29
+ wmi = WmiLite::Wmi.new
30
+ processors = wmi.instances_of('Win32_Processor')
31
+
32
+ processors.find(:all).each do |processor|
30
33
  #
31
34
  # On Windows Server 2003 R2 (i.e. 5.2.*), numberofcores property
32
35
  # doesn't exist on the Win32_Processor class unless the user has
@@ -39,7 +42,7 @@ Ohai.plugin(:CPU) do
39
42
 
40
43
  number_of_cores = nil
41
44
  begin
42
- number_of_cores = processor.numberofcores
45
+ number_of_cores = processor['numberofcores']
43
46
  cpu_number += number_of_cores
44
47
  rescue NoMethodError => e
45
48
  Ohai::Log.info("Can not find numberofcores property on Win32_Processor. Consider applying this patch: http://support.microsoft.com/kb/932370")
@@ -48,16 +51,16 @@ Ohai.plugin(:CPU) do
48
51
  current_cpu = index.to_s
49
52
  index += 1
50
53
  cpuinfo[current_cpu] = Mash.new
51
- cpuinfo[current_cpu]["vendor_id"] = processor.manufacturer
52
- cpuinfo[current_cpu]["family"] = processor.family.to_s
53
- cpuinfo[current_cpu]["model"] = processor.revision.to_s
54
- cpuinfo[current_cpu]["stepping"] = processor.stepping
55
- cpuinfo[current_cpu]["physical_id"] = processor.deviceid
54
+ cpuinfo[current_cpu]["vendor_id"] = processor['manufacturer']
55
+ cpuinfo[current_cpu]["family"] = processor['family'].to_s
56
+ cpuinfo[current_cpu]["model"] = processor['revision'].to_s
57
+ cpuinfo[current_cpu]["stepping"] = processor['stepping']
58
+ cpuinfo[current_cpu]["physical_id"] = processor['deviceid']
56
59
  #cpuinfo[current_cpu]["core_id"] = XXX
57
60
  cpuinfo[current_cpu]["cores"] = number_of_cores
58
- cpuinfo[current_cpu]["model_name"] = processor.description
59
- cpuinfo[current_cpu]["mhz"] = processor.maxclockspeed.to_s
60
- cpuinfo[current_cpu]["cache_size"] = "#{processor.l2cachesize} KB"
61
+ cpuinfo[current_cpu]["model_name"] = processor['description']
62
+ cpuinfo[current_cpu]["mhz"] = processor['maxclockspeed'].to_s
63
+ cpuinfo[current_cpu]["cache_size"] = "#{processor['l2cachesize']} KB"
61
64
  #cpuinfo[current_cpu]["flags"] = XXX
62
65
  end
63
66
 
@@ -20,20 +20,24 @@ Ohai.plugin(:Filesystem) do
20
20
  provides "filesystem"
21
21
 
22
22
  collect_data(:windows) do
23
- require 'ruby-wmi'
23
+
24
+ require 'wmi-lite/wmi'
24
25
 
25
26
  fs = Mash.new
26
27
  ld_info = Mash.new
27
28
 
29
+ wmi = WmiLite::Wmi.new
30
+
28
31
  # Grab filesystem data from WMI
29
32
  # Note: we should really be parsing Win32_Volume and Win32_Mapped drive
30
- disks = WMI::Win32_LogicalDisk.find(:all)
33
+ disks = wmi.instances_of('Win32_LogicalDisk')
34
+
31
35
  disks.each do |disk|
32
- filesystem = disk.DeviceID
36
+ filesystem = disk['deviceid']
33
37
  fs[filesystem] = Mash.new
34
38
  ld_info[filesystem] = Mash.new
35
- disk.properties_.each do |p|
36
- ld_info[filesystem][p.name.wmi_underscore.to_sym] = disk.send(p.name)
39
+ disk.wmi_ole_object.properties_.each do |p|
40
+ ld_info[filesystem][p.name.wmi_underscore.to_sym] = disk[p.name.downcase]
37
41
  end
38
42
  fs[filesystem][:kb_size] = ld_info[filesystem][:size].to_i / 1000
39
43
  fs[filesystem][:kb_available] = ld_info[filesystem][:free_space].to_i / 1000
@@ -26,7 +26,8 @@ Ohai.plugin(:Network) do
26
26
  end
27
27
 
28
28
  collect_data(:windows) do
29
- require 'ruby-wmi'
29
+
30
+ require 'wmi-lite/wmi'
30
31
 
31
32
  iface = Mash.new
32
33
  iface_config = Mash.new
@@ -37,22 +38,28 @@ Ohai.plugin(:Network) do
37
38
  counters[:network] = Mash.new unless counters[:network]
38
39
 
39
40
  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394217%28v=vs.85%29.aspx
40
- adapters = WMI::Win32_NetworkAdapterConfiguration.find(:all)
41
+ wmi = WmiLite::Wmi.new
42
+
43
+ adapters = wmi.instances_of('Win32_NetworkAdapterConfiguration')
44
+
41
45
  adapters.each do |adapter|
42
- i = adapter.Index
46
+
47
+ i = adapter['index']
43
48
  iface_config[i] = Mash.new
44
- adapter.properties_.each do |p|
45
- iface_config[i][p.name.wmi_underscore.to_sym] = adapter.invoke(p.name)
49
+ adapter.wmi_ole_object.properties_.each do |p|
50
+ iface_config[i][p.name.wmi_underscore.to_sym] = adapter[p.name.downcase]
46
51
  end
47
52
  end
48
53
 
49
54
  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
50
- adapters = WMI::Win32_NetworkAdapter.find(:all)
55
+
56
+ adapters = wmi.instances_of('Win32_NetworkAdapter')
57
+
51
58
  adapters.each do |adapter|
52
- i = adapter.Index
59
+ i = adapter['index']
53
60
  iface_instance[i] = Mash.new
54
- adapter.properties_.each do |p|
55
- iface_instance[i][p.name.wmi_underscore.to_sym] = adapter.invoke(p.name)
61
+ adapter.wmi_ole_object.properties_.each do |p|
62
+ iface_instance[i][p.name.wmi_underscore.to_sym] = adapter[p.name.downcase]
56
63
  end
57
64
  end
58
65
 
@@ -0,0 +1,35 @@
1
+ # Author:: Lamont Granquist (<lamont@opscode.com>)
2
+ #
3
+ # Copyright:: Copyright (c) 2013-14 Chef Software, Inc.
4
+ #
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ # Copied from chef/lib/chef/util/selinux.rb
20
+
21
+ module Ohai
22
+ module Util
23
+ module FileHelper
24
+ def which(cmd)
25
+ paths = ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ]
26
+ paths.each do |path|
27
+ filename = File.join(path, cmd)
28
+ return filename if File.executable?(filename)
29
+ end
30
+ false
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -18,5 +18,5 @@
18
18
 
19
19
  module Ohai
20
20
  OHAI_ROOT = File.expand_path(File.dirname(__FILE__))
21
- VERSION = '7.0.4'
21
+ VERSION = '7.2.0.alpha.0'
22
22
  end
@@ -0,0 +1,68 @@
1
+ #
2
+ # Author:: Tim Smith <tsmith@limelight.com>
3
+ # Copyright:: Copyright (c) 2014 Limelight Networks, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
20
+
21
+ describe Ohai::System, "FreeBSD cpu plugin" do
22
+ before(:each) do
23
+ @plugin = get_plugin("freebsd/cpu")
24
+ @plugin.stub(:collect_os).and_return(:freebsd)
25
+ @plugin.stub(:shell_out).with("sysctl -n hw.ncpu").and_return(mock_shell_out(0, "2", ""))
26
+ @double_file = double("/var/run/dmesg.boot")
27
+ @double_file.stub(:each).
28
+ and_yield('CPU: Intel(R) Core(TM) i7-3615QM CPU @ 2.30GHz (3516.61-MHz K8-class CPU)').
29
+ and_yield(' Origin = "GenuineIntel" Id = 0x306a9 Family = 6 Model = 3a Stepping = 9').
30
+ and_yield(' Features=0x783fbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE,SSE2>').
31
+ and_yield(' Features2=0x209<SSE3,MON,SSSE3>').
32
+ and_yield(' AMD Features=0x28100800<SYSCALL,NX,RDTSCP,LM>').
33
+ and_yield(' AMD Features2=0x1<LAHF>').
34
+ and_yield(' TSC: P-state invariant')
35
+ File.stub(:open).with("/var/run/dmesg.boot").and_return(@double_file)
36
+ end
37
+
38
+ it "detects all CPU flags" do
39
+ @plugin.run
40
+ @plugin[:cpu][:flags].should == %w{fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse sse2 sse3 mon ssse3 syscall nx rdtscp lm lahf}
41
+ end
42
+
43
+ it "detects all CPU model_name" do
44
+ @plugin.run
45
+ @plugin[:cpu][:model_name].should == "Intel(R) Core(TM) i7-3615QM CPU @ 2.30GHz"
46
+ end
47
+
48
+ it "detects all CPU mhz" do
49
+ @plugin.run
50
+ @plugin[:cpu][:mhz].should == "3516.61"
51
+ end
52
+
53
+ it "detects all CPU vendor_id" do
54
+ @plugin.run
55
+ @plugin[:cpu][:vendor_id].should == "GenuineIntel"
56
+ end
57
+
58
+ it "detects all CPU stepping" do
59
+ @plugin.run
60
+ @plugin[:cpu][:stepping].should == "9"
61
+ end
62
+
63
+ it "detects all CPU total" do
64
+ @plugin.run
65
+ @plugin[:cpu][:total].should == "2"
66
+ end
67
+
68
+ end
@@ -6,9 +6,9 @@
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
8
8
  # You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
-
20
19
  require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
21
20
 
22
21
  describe Ohai::System, "FreeBSD virtualization plugin" do
@@ -47,7 +46,6 @@ describe Ohai::System, "FreeBSD virtualization plugin" do
47
46
  end
48
47
  end
49
48
 
50
-
51
49
  context "when on a virtualbox guest" do
52
50
  before do
53
51
  @vbox_guest = <<-OUT
@@ -55,7 +53,7 @@ Id Refs Address Size Name
55
53
  1 40 0xffffffff80100000 d20428 kernel
56
54
  7 3 0xffffffff81055000 41e88 vboxguest.ko
57
55
  OUT
58
- @plugin.stub(:shell_out).with("#{ Ohai.abs_path( "/sbin/kldstat" )}").and_return(mock_shell_out(0, @vbox_guest, ""))
56
+ @plugin.stub(:shell_out).with("#{ Ohai.abs_path("/sbin/kldstat")}").and_return(mock_shell_out(0, @vbox_guest, ""))
59
57
  end
60
58
 
61
59
  it "detects we are a guest" do
@@ -84,16 +82,14 @@ OUT
84
82
 
85
83
  context "when on a QEMU guest" do
86
84
  it "detects we are a guest" do
87
- @qemu_guest = 'QEMU Virtual CPU version (cpu64-rhel6) ("GenuineIntel" 686-class)'
88
- @plugin.stub(:shell_out).with("sysctl -n hw.model").and_return(mock_shell_out(0, @qemu_guest, ""))
89
- @plugin.run
90
- @plugin[:virtualization][:system].should == "kvm"
91
- @plugin[:virtualization][:role].should == "guest"
85
+ [ 'Common KVM processor', 'QEMU Virtual CPU version (cpu64-rhel6) ("GenuineIntel" 686-class)', 'Common 32-bit KVM processor'].each do |kvm_string|
86
+ @plugin.stub(:shell_out).with("sysctl -n hw.model").and_return(mock_shell_out(0, kvm_string, ""))
87
+ @plugin.run
88
+ @plugin[:virtualization][:system].should == "kvm"
89
+ @plugin[:virtualization][:role].should == "guest"
90
+ end
92
91
  end
93
92
  end
94
93
 
95
94
  # TODO upfactor tests from linux virtualization plugin for dmidecode
96
95
  end
97
-
98
-
99
-
@@ -37,33 +37,44 @@ describe Ohai::System, "plugin kernel" do
37
37
 
38
38
  describe "when running on windows", :windows_only do
39
39
  before do
40
- require 'ruby-wmi'
40
+ require 'wmi-lite/wmi'
41
41
 
42
42
  @ohai_system = Ohai::System.new
43
43
  @plugin = get_plugin("kernel", @ohai_system)
44
44
 
45
- caption = double("WIN32OLE", :name => "caption")
46
- version = double("WIN32OLE", :name => "version")
45
+ # Mock a Win32_OperatingSystem OLE32 WMI object
46
+ caption = double("WIN32OLE", :name => "Caption")
47
+ version = double("WIN32OLE", :name => "Version")
47
48
  build_number = double("WIN32OLE", :name => "BuildNumber")
48
49
  csd_version = double("WIN32OLE", :name => "CsdVersion")
49
50
  os_type = double("WIN32OLE", :name => "OsType")
50
51
  os_properties = [ caption, version, build_number, csd_version, os_type ]
51
52
 
52
- os = double("WIN32OLE",
53
- :properties_ => os_properties,
54
- :caption => "Microsoft Windows 7 Ultimate",
55
- :version => "6.1.7601",
56
- :BuildNumber => "7601",
57
- :CsdVersion => "Service Pack 1",
58
- :OsType => 18)
59
- WMI::Win32_OperatingSystem.should_receive(:find).with(:first).and_return(os)
53
+ os = double( "WIN32OLE",
54
+ :properties_ => os_properties)
55
+
56
+ os.stub(:invoke).with(build_number.name).and_return('7601')
57
+ os.stub(:invoke).with(csd_version.name).and_return('Service Pack 1')
58
+ os.stub(:invoke).with(os_type.name).and_return(18)
59
+ os.stub(:invoke).with(caption.name).and_return('Microsoft Windows 7 Ultimate')
60
+ os.stub(:invoke).with(version.name).and_return('6.1.7601')
61
+
62
+ os_wmi = WmiLite::Wmi::Instance.new(os)
63
+
64
+ WmiLite::Wmi.any_instance.should_receive(:first_of).with('Win32_OperatingSystem').and_return(os_wmi)
65
+
66
+ # Mock a Win32_ComputerSystem OLE32 WMI object
67
+ x64_system_type = 'x64-based PC'
60
68
 
61
69
  cs = double("WIN32OLE",
62
- :properties_ => [ double("WIN32OLE", :name => "SystemType") ],
63
- :SystemType => "x64-based PC")
64
- WMI::Win32_ComputerSystem.should_receive(:find).with(:first).and_return(cs)
70
+ :properties_ => [ double("WIN32OLE", :name => "SystemType") ])
71
+
72
+ cs.stub(:invoke).with('SystemType').and_return(x64_system_type)
73
+
74
+ cs_wmi = WmiLite::Wmi::Instance.new(cs)
65
75
 
66
- WMI::Win32_PnPSignedDriver.should_receive(:find).with(:all).and_return([ ])
76
+ WmiLite::Wmi.any_instance.should_receive(:first_of).with('Win32_ComputerSystem').and_return(cs_wmi)
77
+ WmiLite::Wmi.any_instance.should_receive(:instances_of).with('Win32_PnPSignedDriver').and_return([])
67
78
 
68
79
  @plugin.run
69
80
  end
@@ -0,0 +1,104 @@
1
+ #
2
+ # Author:: Tim Smith <tsmith@limelight.com>
3
+ # Copyright:: Copyright (c) 2014 Limelight Networks, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
20
+
21
+ describe Ohai::System, "Linux Mdadm Plugin" do
22
+ before(:each) do
23
+ @md0 = <<-MD
24
+ /dev/md0:
25
+ Version : 1.2
26
+ Creation Time : Thu Jan 30 03:11:40 2014
27
+ Raid Level : raid10
28
+ Array Size : 2929893888 (2794.16 GiB 3000.21 GB)
29
+ Used Dev Size : 976631296 (931.39 GiB 1000.07 GB)
30
+ Raid Devices : 6
31
+ Total Devices : 6
32
+ Persistence : Superblock is persistent
33
+
34
+ Update Time : Tue May 6 23:30:32 2014
35
+ State : clean
36
+ Active Devices : 6
37
+ Working Devices : 6
38
+ Failed Devices : 0
39
+ Spare Devices : 0
40
+
41
+ Layout : near=2
42
+ Chunk Size : 256K
43
+
44
+ Name : host.therealtimsmith.com:3 (local to host host.therealtimsmith.com)
45
+ UUID : 5ed74d5b:70bfe21d:8cd57792:c1e13d65
46
+ Events : 155
47
+
48
+ Number Major Minor RaidDevice State
49
+ 0 8 32 0 active sync /dev/sdc
50
+ 1 8 48 1 active sync /dev/sdd
51
+ 2 8 64 2 active sync /dev/sde
52
+ 3 8 80 3 active sync /dev/sdf
53
+ 4 8 96 4 active sync /dev/sdg
54
+ 5 8 112 5 active sync /dev/sdh
55
+ MD
56
+ @plugin = get_plugin("linux/mdadm")
57
+ @plugin.stub(:collect_os).and_return(:linux)
58
+ @double_file = double("/proc/mdstat")
59
+ @double_file.stub(:each).
60
+ and_yield("Personalities : [raid1] [raid6] [raid5] [raid4] [linear] [multipath] [raid0] [raid10]").
61
+ and_yield("md0 : active raid10 sdh[5] sdg[4] sdf[3] sde[2] sdd[1] sdc[0]").
62
+ and_yield(" 2929893888 blocks super 1.2 256K chunks 2 near-copies [6/6] [UUUUUU]")
63
+ File.stub(:open).with("/proc/mdstat").and_return(@double_file)
64
+ File.stub(:exist?).with("/proc/mdstat").and_return(true)
65
+ @plugin.stub(:shell_out).with("mdadm --detail /dev/md0").and_return(mock_shell_out(0, @md0, ""))
66
+ end
67
+
68
+ describe "gathering Mdadm information via /proc/mdstat and mdadm" do
69
+
70
+ it "should not raise an error" do
71
+ lambda { @plugin.run }.should_not raise_error
72
+ end
73
+
74
+ it "should detect raid level" do
75
+ @plugin.run
76
+ @plugin[:mdadm][:md0][:level].should == 10
77
+ end
78
+
79
+ it "should detect raid state" do
80
+ @plugin.run
81
+ @plugin[:mdadm][:md0][:state].should == "clean"
82
+ end
83
+
84
+ it "should detect raid size" do
85
+ @plugin.run
86
+ @plugin[:mdadm][:md0][:size].should == 2794.16
87
+ end
88
+
89
+ it "should detect raid metadata level" do
90
+ @plugin.run
91
+ @plugin[:mdadm][:md0][:version].should == 1.2
92
+ end
93
+
94
+ device_counts = { :raid => 6, :total => 6, :active => 6, :working => 6, :failed => 0, :spare => 0 }
95
+ device_counts.each_pair do |item, expected_value|
96
+ it "should detect device count of \"#{item}\"" do
97
+ @plugin.run
98
+ @plugin[:mdadm][:md0][:device_counts][item].should == expected_value
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ end