diff options
Diffstat (limited to 'lib/chef/provider/mount/mount.rb')
-rw-r--r-- | lib/chef/provider/mount/mount.rb | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb new file mode 100644 index 0000000000..9a85a9058a --- /dev/null +++ b/lib/chef/provider/mount/mount.rb @@ -0,0 +1,252 @@ +# +# Author:: Joshua Timberman (<joshua@opscode.com>) +# Copyright:: Copyright (c) 2009 Opscode, 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 'chef/provider/mount' +require 'chef/log' +require 'chef/mixin/shell_out' + +class Chef + class Provider + class Mount + class Mount < Chef::Provider::Mount + include Chef::Mixin::ShellOut + + def initialize(new_resource, run_context) + super + @real_device = nil + end + attr_accessor :real_device + + def load_current_resource + @current_resource = Chef::Resource::Mount.new(@new_resource.name) + @current_resource.mount_point(@new_resource.mount_point) + @current_resource.device(@new_resource.device) + mounted? + enabled? + end + + def mountable? + # only check for existence of non-remote devices + if (device_should_exist? && !::File.exists?(device_real) ) + raise Chef::Exceptions::Mount, "Device #{@new_resource.device} does not exist" + elsif( !::File.exists?(@new_resource.mount_point) ) + raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist" + end + return true + end + + def enabled? + # Check to see if there is a entry in /etc/fstab. Last entry for a volume wins. + enabled = false + ::File.foreach("/etc/fstab") do |line| + case line + when /^[#\s]/ + next + when /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ + enabled = true + @current_resource.fstype($1) + @current_resource.options($2) + @current_resource.dump($3.to_i) + @current_resource.pass($4.to_i) + Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab") + next + when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/ + enabled = false + Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab") + end + end + @current_resource.enabled(enabled) + end + + def mounted? + mounted = false + shell_out!("mount").stdout.each_line do |line| + case line + when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(@new_resource.mount_point)}/ + mounted = true + Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}") + when /^([\/\w])+\son\s#{Regexp.escape(@new_resource.mount_point)}\s+/ + mounted = false + Chef::Log.debug("Special device #{$~[1]} mounted as #{@new_resource.mount_point}") + end + end + @current_resource.mounted(mounted) + end + + def mount_fs + unless @current_resource.mounted + mountable? + command = "mount -t #{@new_resource.fstype}" + command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty? + command << case @new_resource.device_type + when :device + " #{device_real}" + when :label + " -L #{@new_resource.device}" + when :uuid + " -U #{@new_resource.device}" + end + command << " #{@new_resource.mount_point}" + shell_out!(command) + Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}") + else + Chef::Log.debug("#{@new_resource} is already mounted at #{@new_resource.mount_point}") + end + end + + def umount_fs + if @current_resource.mounted + shell_out!("umount #{@new_resource.mount_point}") + Chef::Log.debug("#{@new_resource} is no longer mounted at #{@new_resource.mount_point}") + else + Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point}") + end + end + + def remount_fs + if @current_resource.mounted and @new_resource.supports[:remount] + shell_out!("mount -o remount #{@new_resource.mount_point}") + @new_resource.updated_by_last_action(true) + Chef::Log.debug("#{@new_resource} is remounted at #{@new_resource.mount_point}") + elsif @current_resource.mounted + umount_fs + sleep 1 + mount_fs + else + Chef::Log.debug("#{@new_resource} is not mounted at #{@new_resource.mount_point} - nothing to do") + end + end + + def enable_fs + if @current_resource.enabled && mount_options_unchanged? + Chef::Log.debug("#{@new_resource} is already enabled - nothing to do") + return nil + end + + if @current_resource.enabled + # The current options don't match what we have, so + # disable, then enable. + disable_fs + end + ::File.open("/etc/fstab", "a") do |fstab| + fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}") + Chef::Log.debug("#{@new_resource} is enabled at #{@new_resource.mount_point}") + end + end + + def disable_fs + if @current_resource.enabled + contents = [] + + found = false + ::File.readlines("/etc/fstab").reverse_each do |line| + if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}/ + found = true + Chef::Log.debug("#{@new_resource} is removed from fstab") + next + else + contents << line + end + end + + ::File.open("/etc/fstab", "w") do |fstab| + contents.reverse_each { |line| fstab.puts line} + end + else + Chef::Log.debug("#{@new_resource} is not enabled - nothing to do") + end + end + + def network_device? + @new_resource.device =~ /:/ || @new_resource.device =~ /\/\// + end + + def device_should_exist? + ( not network_device? ) && + ( not %w[ tmpfs fuse ].include? @new_resource.fstype ) + end + + private + + def device_fstab + case @new_resource.device_type + when :device + @new_resource.device + when :label + "LABEL=#{@new_resource.device}" + when :uuid + "UUID=#{@new_resource.device}" + end + end + + def device_real + if @real_device == nil + if @new_resource.device_type == :device + @real_device = @new_resource.device + else + @real_device = "" + status = popen4("/sbin/findfs #{device_fstab}") do |pid, stdin, stdout, stderr| + device_line = stdout.first # stdout.first consumes + @real_device = device_line.chomp unless device_line.nil? + end + end + end + @real_device + end + + def device_logstring + case @new_resource.device_type + when :device + "#{device_real}" + when :label + "#{device_real} with label #{@new_resource.device}" + when :uuid + "#{device_real} with uuid #{@new_resource.device}" + end + end + + def device_mount_regex + if network_device? + # ignore trailing slash + Regexp.escape(device_real)+"/?" + elsif ::File.symlink?(device_real) + "(?:#{Regexp.escape(device_real)})|(?:#{Regexp.escape(::File.readlink(device_real))})" + else + Regexp.escape(device_real) + end + end + + def device_fstab_regex + if @new_resource.device_type == :device + device_mount_regex + else + device_fstab + end + end + + def mount_options_unchanged? + @current_resource.fstype == @new_resource.fstype and + @current_resource.options == @new_resource.options and + @current_resource.dump == @new_resource.dump and + @current_resource.pass == @new_resource.pass + end + + end + end + end +end |