lib/arborist/monitor/snmp/disk.rb
changeset 27 02b8e772e0c5
parent 26 54f2f57cc0b0
child 29 40bcd1565627
equal deleted inserted replaced
26:54f2f57cc0b0 27:02b8e772e0c5
    17 	# OIDS required to pull disk information from net-snmp.
    17 	# OIDS required to pull disk information from net-snmp.
    18 	#
    18 	#
    19 	STORAGE_NET_SNMP = {
    19 	STORAGE_NET_SNMP = {
    20 		path: '1.3.6.1.4.1.2021.9.1.2',
    20 		path: '1.3.6.1.4.1.2021.9.1.2',
    21 		percent: '1.3.6.1.4.1.2021.9.1.9',
    21 		percent: '1.3.6.1.4.1.2021.9.1.9',
    22 		type: '1.3.6.1.2.1.25.3.8.1.4'
    22 		type: '1.3.6.1.2.1.25.3.8.1.4',
       
    23 		access: '1.3.6.1.2.1.25.3.8.1.5'
    23 	}
    24 	}
    24 
    25 
    25 	# The OID that matches a local windows hard disk.
    26 	# The OID that matches a local windows hard disk.
    26 	#
    27 	#
    27 	WINDOWS_DEVICES = [
    28 	WINDOWS_DEVICES = [
    39 	}
    40 	}
    40 
    41 
    41 	# The fallback warning capacity.
    42 	# The fallback warning capacity.
    42 	WARN_AT = 90
    43 	WARN_AT = 90
    43 
    44 
       
    45 	# Access mode meanings
       
    46 	ACCESS_READWRITE = 1
       
    47 	ACCESS_READONLY  = 2
    44 
    48 
    45 	# Configurability API
    49 	# Configurability API
    46 	#
    50 	#
    47 	configurability( 'arborist.snmp.disk' ) do
    51 	configurability( 'arborist.snmp.disk' ) do
    48 		# What percentage qualifies as a warning
    52 		# What percentage qualifies as a warning
    62 		setting :exclude,
    66 		setting :exclude,
    63 			default: [ '^/dev(/.+)?$', '/dev$', '^/net(/.+)?$', '/proc$', '^/run$', '^/sys/', '/sys$' ] do |val|
    67 			default: [ '^/dev(/.+)?$', '/dev$', '^/net(/.+)?$', '/proc$', '^/run$', '^/sys/', '/sys$' ] do |val|
    64 			mounts = Array( val ).map{|m| Regexp.new(m) }
    68 			mounts = Array( val ).map{|m| Regexp.new(m) }
    65 			Regexp.union( mounts )
    69 			Regexp.union( mounts )
    66 		end
    70 		end
       
    71 
       
    72 		# Paths to alert for read-only status
       
    73 		setting :readonly_include, default: [ '^/$' ] do |val|
       
    74 			mounts = Array( val ).map{|m| Regexp.new(m) }
       
    75 			Regexp.union( mounts )
       
    76 		end
    67 	end
    77 	end
    68 
    78 
    69 
    79 
    70 	### Return the properties used by this monitor.
    80 	### Return the properties used by this monitor.
    71 	###
    81 	###
   102 	def gather_disks( host, snmp )
   112 	def gather_disks( host, snmp )
   103 		current_mounts = self.system =~ /windows\s+/i ? self.windows_disks( snmp ) : self.unix_disks( snmp )
   113 		current_mounts = self.system =~ /windows\s+/i ? self.windows_disks( snmp ) : self.unix_disks( snmp )
   104 		config         = self.identifiers[ host ].last['config'] || {}
   114 		config         = self.identifiers[ host ].last['config'] || {}
   105 		warn_at        = config[ 'warn_at' ] || self.class.warn_at
   115 		warn_at        = config[ 'warn_at' ] || self.class.warn_at
   106 
   116 
   107 		self.log.warn self.identifiers[ host ]
   117 		self.log.debug self.identifiers[ host ]
   108 
   118 
   109 		includes = self.format_mounts( config, 'include' ) || self.class.include
   119 		includes = self.format_mounts( config, 'include' ) || self.class.include
   110 		excludes = self.format_mounts( config, 'exclude' ) || self.class.exclude
   120 		excludes = self.format_mounts( config, 'exclude' ) || self.class.exclude
   111 
   121 
   112 		current_mounts.reject! do |path, percentage|
   122 		current_mounts.reject! do |path, data|
   113 			path = path.to_s
   123 			path = path.to_s
   114 			excludes.match( path ) || ( includes && ! includes.match( path ) )
   124 			excludes.match( path ) || ( includes && ! includes.match( path ) )
   115 		end
   125 		end
   116 
   126 
   117 		errors   = []
   127 		errors   = []
   118 		warnings = []
   128 		warnings = []
   119 		current_mounts.each_pair do |path, percentage|
   129 		current_mounts.each_pair do |path, data|
   120 			warn = if warn_at.is_a?( Hash )
   130 			warn = if warn_at.is_a?( Hash )
   121 				warn_at[ path ] || WARN_AT
   131 				warn_at[ path ] || WARN_AT
   122 			else
   132 			else
   123 				warn_at
   133 				warn_at
   124 			end
   134 			end
   125 
   135 
   126 			self.log.debug "%s:%s -> at %d, warn at %d" % [ host, path, percentage, warn ]
   136 			self.log.debug "%s:%s -> %p, warn at %d" % [ host, path, data, warn ]
   127 
   137 
   128 			if percentage >= warn.to_i
   138 			if data[ :capacity ] >= warn.to_i
   129 				if percentage >= 100
   139 				if data[ :capacity ] >= 100
   130 					errors << "%s at %d%% capacity" % [ path, percentage ]
   140 					errors << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
   131 				else
   141 				else
   132 					warnings << "%s at %d%% capacity" % [ path, percentage ]
   142 					warnings << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
   133 				end
   143 				end
       
   144 			end
       
   145 
       
   146 			if self.class.readonly_include.match( path ) && data[ :accessmode ] == ACCESS_READONLY
       
   147 				errors << "%s is mounted read-only." % [ path ]
   134 			end
   148 			end
   135 		end
   149 		end
   136 
   150 
   137 		# Remove any past mounts that configuration exclusions should
   151 		# Remove any past mounts that configuration exclusions should
   138 		# now omit.
   152 		# now omit.
   183 		disks = {}
   197 		disks = {}
   184 		paths.each_with_index do |path, idx|
   198 		paths.each_with_index do |path, idx|
   185 			next if totals[ idx ].zero?
   199 			next if totals[ idx ].zero?
   186 			next unless types[ idx ]
   200 			next unless types[ idx ]
   187 			disks[ path ] ||= {}
   201 			disks[ path ] ||= {}
   188 			disks[ path ] = (( used[idx].to_f / totals[idx] ) * 100).round( 1 )
   202 			disks[ path ][ :capacity ] = (( used[idx].to_f / totals[idx] ) * 100).round( 1 )
   189 		end
   203 		end
   190 
   204 
   191 		return disks
   205 		return disks
   192 	end
   206 	end
   193 
   207 
   194 
   208 
   195 	### Fetch information for Unix/MacOS systems.
   209 	### Fetch information for Unix/MacOS systems.
   196 	###
   210 	###
   197 	def unix_disks( snmp )
   211 	def unix_disks( snmp )
   198 		oids = [ STORAGE_NET_SNMP[:path], STORAGE_NET_SNMP[:percent] ]
   212 		oids = [ STORAGE_NET_SNMP[:path], STORAGE_NET_SNMP[:percent], STORAGE_NET_SNMP[:access] ]
   199 		paths = snmp.walk( oid: oids.first ).each_with_object( [] ) do |(_, value), acc|
   213 		paths = snmp.walk( oid: STORAGE_NET_SNMP[:path] ).each_with_object( [] ) do |(_, value), acc|
   200 			acc << value
   214 			acc << value
   201 		end
   215 		end
   202 		capacities = snmp.walk( oid: oids.last ).each_with_object( [] ) do |(_, value), acc|
   216 		capacities = snmp.walk( oid: STORAGE_NET_SNMP[:percent] ).each_with_object( [] ) do |(_, value), acc|
   203 			acc << value
   217 			acc << value
   204 		end
   218 		end
   205 
   219 		accessmodes = snmp.walk( oid: STORAGE_NET_SNMP[:access] ).each_with_object( [] ) do |(_, value), acc|
   206 		pairs = paths.zip( capacities )
   220 			acc << value
   207 		return Hash[ *pairs.flatten ]
   221 		end
       
   222 
       
   223 		pairs = paths.each_with_object( {} ).with_index do |(p, acc), idx|
       
   224 			acc[p] = { capacity: capacities[idx], accessmode: accessmodes[idx] }
       
   225 		end
       
   226 		return pairs
   208 	end
   227 	end
   209 
   228 
   210 end # class Arborist::Monitor::SNMP::Disk
   229 end # class Arborist::Monitor::SNMP::Disk
   211 
   230