summaryrefslogtreecommitdiff
path: root/lib/chef/provider/mount/solaris.rb
blob: fdcea7501f3a5b974f530fa05cfd802866d94ab1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# Author:: Hugo Fichter
# Based on previous work from Joshua Timberman
# (See original header below)
# License:: Apache License, Version 2.0

#
# 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 Solaris < Chef::Provider::Mount
        include Chef::Mixin::ShellOut

        def initialize(new_resource, run_context)
          if new_resource.device_type == :device
            Chef::Log.error("Mount resource can only be of of device_type ':device' on Solaris")
          end
          super
        end

        def load_current_resource
          self.current_resource = Chef::Resource::Mount.new(new_resource.name)
          current_resource.mount_point(new_resource.mount_point)
          current_resource.device(new_resource.device)
          current_resource.mounted(mounted?)
          current_resource.enabled(enabled?)
        end

        protected

        def mount_fs
          mountable?
          actual_options = unless new_resource.options.nil?
                             new_resource.options(new_resource.options.delete("noauto"))
                           end
          command = "mount -F #{new_resource.fstype}"
          command << " -o #{actual_options.join(',')}" unless actual_options.nil?  || actual_options.empty?
          command << " #{new_resource.device}"
          command << " #{new_resource.mount_point}"
          shell_out!(command)
        end

        def umount_fs
          shell_out!("umount #{new_resource.mount_point}")
        end

        def remount_fs
          shell_out!("mount -o remount #{new_resource.mount_point}")
        end

        def enable_fs
          if !mount_options_unchanged?
            # Options changed: disable first, then re-enable.
            disable_fs if current_resource.enabled
          end

          # FIXME: open a tempfile, write to it, close it, then rename it.
          ::File.open("/etc/vfstab", "a") do |fstab|
            auto = new_resource.options.nil? || ! new_resource.options.include?("noauto")
            actual_options = unless new_resource.options.nil?
                               new_resource.options.delete("noauto")
                               new_resource.options
                             end
            fstab.puts("#{new_resource.device}\t-\t#{new_resource.mount_point}\t#{new_resource.fstype}\t#{new_resource.pass == 0 ? "-" : new_resource.pass}\t#{ auto ? :yes : :no }\t #{(actual_options.nil? || actual_options.empty?) ? "-" : actual_options.join(',')}")
          end
        end

        def disable_fs
          contents = []

          # FIXME: open a tempfile, write to it, close it, then rename it.
          found = false
          ::File.readlines("/etc/vfstab").reverse_each do |line|
            if !found && line =~ /^#{device_vfstab_regex}\s+[-\/\w]+\s+#{Regexp.escape(new_resource.mount_point)}/
              found = true
              Chef::Log.debug("#{new_resource} is removed from vfstab")
              next
            else
              contents << line
            end
          end

          ::File.open("/etc/vfstab", "w") do |fstab|
            contents.reverse_each { |line| fstab.puts line}
          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

        private

        # FIXME: should be mountable! and probably should be in define_resource_requirements
        def mountable?
          # only check for existence of non-remote devices
          if (device_should_exist? && !::File.exists?(new_resource.device) )
            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
          true
        end

        def enabled?
          # Check to see if there is a entry in /etc/vfstab. Last entry for a volume wins.
          enabled = false
          ::File.foreach("/etc/vfstab") do |line|
            case line
            when /^[#\s]/
              next
              # solaris /etc/vfstab format:
              # device         device          mount           FS      fsck    mount   mount
              # to mount       to fsck         point           type    pass    at boot options
            when /^#{device_vfstab_regex}\s+[-\/\w]+\s+#{Regexp.escape(new_resource.mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
              enabled = true
              current_resource.fstype($1)
              # Store the 'mount at boot' column from vfstab as the 'noauto' option
              # in current_resource.options (linux style)
              no_auto_option = ($3 == "yes")
              options = $4
              if no_auto_option
                if options.nil? || options.empty?
                  options = "noauto"
                else
                  options += ",noauto"
                end
              end
              current_resource.options(options)
              if $2 == "-"
                pass = 0
              else
                pass = $2.to_i
              end
              current_resource.pass(pass)
              Chef::Log.debug("Found mount #{new_resource.device} to #{new_resource.mount_point} in /etc/vfstab")
              next
            when /^[-\/\w]+\s+[-\/\w]+\s+#{Regexp.escape(new_resource.mount_point)}\s+/
              enabled = false
              Chef::Log.debug("Found conflicting mount point #{new_resource.mount_point} in /etc/vfstab")
            end
          end
          enabled
        end

        def mounted?
          shell_out!("mount").stdout.each_line do |line|
            # on solaris, 'mount' prints "<mount point> on <device> ...'
            case line
            when /^#{Regexp.escape(new_resource.mount_point)}\s+on\s+#{device_mount_regex}/
              Chef::Log.debug("Special device #{new_resource.device} is mounted as #{new_resource.mount_point}")
              return true
            when /^#{Regexp.escape(new_resource.mount_point)}\son\s([\/\w])+\s+/
              Chef::Log.debug("Special device #{$~[1]} is mounted as #{new_resource.mount_point}")
            end
          end
          return false
        end

        def device_should_exist?
          new_resource.device !~ /:/ && new_resource.device !~ /\/\// && new_resource.fstype != "tmpfs" && new_resource.fstype != 'fuse'
        end

        def device_mount_regex
          ::File.symlink?(new_resource.device) ? "(?:#{Regexp.escape(new_resource.device)})|(?:#{Regexp.escape(::File.readlink(new_resource.device))})" : Regexp.escape(new_resource.device)
        end

        def device_vfstab_regex
          device_mount_regex
        end

      end
    end
  end
end