lib/arborist/monitor/snmp/disk.rb
changeset 27 02b8e772e0c5
parent 26 54f2f57cc0b0
child 29 40bcd1565627
--- a/lib/arborist/monitor/snmp/disk.rb	Tue Apr 28 10:10:19 2020 -0700
+++ b/lib/arborist/monitor/snmp/disk.rb	Mon Jul 27 03:19:20 2020 -0700
@@ -19,7 +19,8 @@
 	STORAGE_NET_SNMP = {
 		path: '1.3.6.1.4.1.2021.9.1.2',
 		percent: '1.3.6.1.4.1.2021.9.1.9',
-		type: '1.3.6.1.2.1.25.3.8.1.4'
+		type: '1.3.6.1.2.1.25.3.8.1.4',
+		access: '1.3.6.1.2.1.25.3.8.1.5'
 	}
 
 	# The OID that matches a local windows hard disk.
@@ -41,6 +42,9 @@
 	# The fallback warning capacity.
 	WARN_AT = 90
 
+	# Access mode meanings
+	ACCESS_READWRITE = 1
+	ACCESS_READONLY  = 2
 
 	# Configurability API
 	#
@@ -64,6 +68,12 @@
 			mounts = Array( val ).map{|m| Regexp.new(m) }
 			Regexp.union( mounts )
 		end
+
+		# Paths to alert for read-only status
+		setting :readonly_include, default: [ '^/$' ] do |val|
+			mounts = Array( val ).map{|m| Regexp.new(m) }
+			Regexp.union( mounts )
+		end
 	end
 
 
@@ -104,34 +114,38 @@
 		config         = self.identifiers[ host ].last['config'] || {}
 		warn_at        = config[ 'warn_at' ] || self.class.warn_at
 
-		self.log.warn self.identifiers[ host ]
+		self.log.debug self.identifiers[ host ]
 
 		includes = self.format_mounts( config, 'include' ) || self.class.include
 		excludes = self.format_mounts( config, 'exclude' ) || self.class.exclude
 
-		current_mounts.reject! do |path, percentage|
+		current_mounts.reject! do |path, data|
 			path = path.to_s
 			excludes.match( path ) || ( includes && ! includes.match( path ) )
 		end
 
 		errors   = []
 		warnings = []
-		current_mounts.each_pair do |path, percentage|
+		current_mounts.each_pair do |path, data|
 			warn = if warn_at.is_a?( Hash )
 				warn_at[ path ] || WARN_AT
 			else
 				warn_at
 			end
 
-			self.log.debug "%s:%s -> at %d, warn at %d" % [ host, path, percentage, warn ]
+			self.log.debug "%s:%s -> %p, warn at %d" % [ host, path, data, warn ]
 
-			if percentage >= warn.to_i
-				if percentage >= 100
-					errors << "%s at %d%% capacity" % [ path, percentage ]
+			if data[ :capacity ] >= warn.to_i
+				if data[ :capacity ] >= 100
+					errors << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
 				else
-					warnings << "%s at %d%% capacity" % [ path, percentage ]
+					warnings << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
 				end
 			end
+
+			if self.class.readonly_include.match( path ) && data[ :accessmode ] == ACCESS_READONLY
+				errors << "%s is mounted read-only." % [ path ]
+			end
 		end
 
 		# Remove any past mounts that configuration exclusions should
@@ -185,7 +199,7 @@
 			next if totals[ idx ].zero?
 			next unless types[ idx ]
 			disks[ path ] ||= {}
-			disks[ path ] = (( used[idx].to_f / totals[idx] ) * 100).round( 1 )
+			disks[ path ][ :capacity ] = (( used[idx].to_f / totals[idx] ) * 100).round( 1 )
 		end
 
 		return disks
@@ -195,16 +209,21 @@
 	### Fetch information for Unix/MacOS systems.
 	###
 	def unix_disks( snmp )
-		oids = [ STORAGE_NET_SNMP[:path], STORAGE_NET_SNMP[:percent] ]
-		paths = snmp.walk( oid: oids.first ).each_with_object( [] ) do |(_, value), acc|
+		oids = [ STORAGE_NET_SNMP[:path], STORAGE_NET_SNMP[:percent], STORAGE_NET_SNMP[:access] ]
+		paths = snmp.walk( oid: STORAGE_NET_SNMP[:path] ).each_with_object( [] ) do |(_, value), acc|
 			acc << value
 		end
-		capacities = snmp.walk( oid: oids.last ).each_with_object( [] ) do |(_, value), acc|
+		capacities = snmp.walk( oid: STORAGE_NET_SNMP[:percent] ).each_with_object( [] ) do |(_, value), acc|
+			acc << value
+		end
+		accessmodes = snmp.walk( oid: STORAGE_NET_SNMP[:access] ).each_with_object( [] ) do |(_, value), acc|
 			acc << value
 		end
 
-		pairs = paths.zip( capacities )
-		return Hash[ *pairs.flatten ]
+		pairs = paths.each_with_object( {} ).with_index do |(p, acc), idx|
+			acc[p] = { capacity: capacities[idx], accessmode: accessmodes[idx] }
+		end
+		return pairs
 	end
 
 end # class Arborist::Monitor::SNMP::Disk