manageiq-smartstate 0.3.4 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile +1 -1
- data/lib/fs/modules/LinuxMount.rb +18 -14
- data/lib/fs/ntfs/data_run.rb +1 -1
- data/lib/manageiq/smartstate/version.rb +1 -1
- data/lib/metadata/util/find_class_methods.rb +133 -0
- data/lib/metadata/util/md5deep.rb +59 -43
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f517cfb3cafa62840376ce516426e2af1b2180a8733cca57f0e6e212a1596f0f
|
4
|
+
data.tar.gz: b443f7a19ecfdfafc1c50432853fa22f789c33674f99cb54873dda3cb21b85ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 807d2fb11644d303a15818020dfed093e28a5111f0bd3ec4944f6ee4ee42b6b8cb626f7322d6b59c78db84056d6665332c5f35fdc1011e15955f5bc458b7631b
|
7
|
+
data.tar.gz: 20e52907b15cf35f57a9f90104a0323b1ff3406b86668b54ed22db68e7408ac9c3cb9efbcb7468742b09c2c3e25add71979583253e658b714577125ff28548fc
|
data/Gemfile
CHANGED
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in manageiq-smartstate.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem "manageiq-gems-pending", :git => "https://github.com/ManageIQ/manageiq-gems-pending.git", :branch => "
|
6
|
+
gem "manageiq-gems-pending", :git => "https://github.com/ManageIQ/manageiq-gems-pending.git", :branch => "ivanchuk"
|
7
7
|
|
8
8
|
# Modified gems for vmware_web_service. Setting sources here since they are git references
|
9
9
|
gem "handsoap", "~>0.2.5", :require => false, :git => "https://github.com/ManageIQ/handsoap.git", :tag => "v0.2.5-5"
|
@@ -309,23 +309,27 @@ module LinuxMount
|
|
309
309
|
#
|
310
310
|
components.each do |c|
|
311
311
|
ncp = File.join(cp, c)
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
312
|
+
cp = follow_all_symlinks(ncp)
|
313
|
+
end
|
314
|
+
cp
|
315
|
+
end
|
316
|
+
|
317
|
+
def follow_all_symlinks(link_ptr)
|
318
|
+
#
|
319
|
+
# Each filesystem knows how to check for,
|
320
|
+
# and read its own links.
|
321
|
+
#
|
322
|
+
no_more_links = false
|
323
|
+
until no_more_links
|
324
|
+
filesys, tmp_link_ptr = getFsPathBase(link_ptr)
|
325
|
+
if filesys.fileSymLink?(tmp_link_ptr)
|
326
|
+
symlink = getSymLink(filesys, tmp_link_ptr)
|
327
|
+
link_ptr = symlink[0, 1] == '/' ? symlink : File.join(File.dirname(link_ptr), symlink)
|
324
328
|
else
|
325
|
-
|
329
|
+
no_more_links = true
|
326
330
|
end
|
327
331
|
end
|
328
|
-
|
332
|
+
link_ptr
|
329
333
|
end
|
330
334
|
|
331
335
|
def getSymLink(fs, p)
|
data/lib/fs/ntfs/data_run.rb
CHANGED
@@ -282,7 +282,7 @@ module NTFS
|
|
282
282
|
|
283
283
|
@runSpec.each_slice(2) do |lcn, len|
|
284
284
|
total_clusters += len
|
285
|
-
next unless total_clusters > start_vcn
|
285
|
+
next unless lcn && len && total_clusters > start_vcn
|
286
286
|
|
287
287
|
start = lcn + (vcn - (total_clusters - len))
|
288
288
|
count = len - (start - lcn)
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module FindClassMethods
|
2
|
+
# Return directory entries matching specified glob pattern
|
3
|
+
#
|
4
|
+
# @param glob_pattern [String] pattern to match
|
5
|
+
# @param flags [Integer] file match flags
|
6
|
+
# @yield block invoked with each match if specified
|
7
|
+
#
|
8
|
+
# @see VfsRealFile.fnmatch
|
9
|
+
# @see FindClassMethods#dir_and_glob which does most of the work regarding globbing
|
10
|
+
# @see FindClassMethods#find which retrieves stats information & dir entries for found files
|
11
|
+
#
|
12
|
+
def self.glob(glob_pattern, filesys, flags = 0)
|
13
|
+
@fs = filesys
|
14
|
+
return [] unless (glob = dir_and_glob(glob_pattern))
|
15
|
+
|
16
|
+
ra = []
|
17
|
+
find(@search_path, glob_depth(glob)) do |p|
|
18
|
+
if (p = check_file(p, glob, flags))
|
19
|
+
block_given? ? yield(p) : ra << p
|
20
|
+
end
|
21
|
+
end
|
22
|
+
ra.sort_by(&:downcase)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Determine if the file returned from "find" will be used or skipped.
|
27
|
+
#
|
28
|
+
def self.check_file(file, glob, flags)
|
29
|
+
return nil if file == @search_path
|
30
|
+
|
31
|
+
if @search_path == File::SEPARATOR
|
32
|
+
file.sub!(File::SEPARATOR, "")
|
33
|
+
else
|
34
|
+
file.sub!("#{@search_path}#{File::SEPARATOR}", "")
|
35
|
+
end
|
36
|
+
|
37
|
+
return nil if file == "" || !File.fnmatch(glob, file, flags)
|
38
|
+
|
39
|
+
@specified_path ? File.join(@specified_path, file) : file
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Modified version of Find.find:
|
44
|
+
# - Accepts only a single path.
|
45
|
+
# - Can be restricted by depth - optimization for glob searches.
|
46
|
+
#
|
47
|
+
# @param path [String] starting directory of the find
|
48
|
+
# @param max_depth [Integer] max number of levels to decend befroelookup
|
49
|
+
# @yield files found
|
50
|
+
#
|
51
|
+
def self.find(path, max_depth = nil)
|
52
|
+
block_given? || (return enum_for(__method__, path, max_depth))
|
53
|
+
|
54
|
+
depths = [0]
|
55
|
+
paths = [path.dup]
|
56
|
+
|
57
|
+
while (file = paths.shift)
|
58
|
+
depth = depths.shift
|
59
|
+
yield file.dup.taint
|
60
|
+
next if max_depth && depth + 1 > max_depth
|
61
|
+
|
62
|
+
get_dir_entries(file).each do |f|
|
63
|
+
f = File.join(file, f)
|
64
|
+
paths.unshift f.untaint
|
65
|
+
depths.unshift depth + 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.get_dir_entries(directory)
|
71
|
+
return [] unless @fs.fileExists?(directory) && @fs.fileDirectory?(directory)
|
72
|
+
|
73
|
+
files = @fs.dirEntries(directory)
|
74
|
+
files -= [".", ".."]
|
75
|
+
files.sort!
|
76
|
+
files.reverse_each
|
77
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
|
78
|
+
$log.info "find: while-loop @fs.dirEntries #{directory} returned an error"
|
79
|
+
[]
|
80
|
+
end
|
81
|
+
|
82
|
+
GLOB_CHARS = '*?[{'.freeze
|
83
|
+
def self.glob_str?(str)
|
84
|
+
str.gsub(/\\./, "X").count(GLOB_CHARS) != 0
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns files matching glob pattern
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
# @param glob_pattern [String,Regex] pattern to search for
|
91
|
+
# @return [String] paths to files found
|
92
|
+
#
|
93
|
+
def self.dir_and_glob(glob_pattern)
|
94
|
+
stripped_path = glob_pattern.sub(/^[a-zA-Z]:/, "")
|
95
|
+
glob_path = Pathname.new(stripped_path)
|
96
|
+
@search_path = File::SEPARATOR
|
97
|
+
@specified_path = File::SEPARATOR
|
98
|
+
|
99
|
+
unless glob_path.absolute?
|
100
|
+
@search_path = Dir.getwd
|
101
|
+
@specified_path = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
components = path_components(glob_path)
|
105
|
+
@search_path = File.expand_path(@search_path, "/")
|
106
|
+
@fs.fileExists?(@search_path) ? File.join(components) : nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.path_components(glob_path, search_path = @search_path)
|
110
|
+
components = glob_path.each_filename.to_a
|
111
|
+
while (comp = components.shift)
|
112
|
+
if glob_str?(comp)
|
113
|
+
components.unshift(comp)
|
114
|
+
break
|
115
|
+
end
|
116
|
+
@search_path = File.join(search_path, comp)
|
117
|
+
@specified_path = @specified_path ? File.join(@specified_path, comp) : comp
|
118
|
+
end
|
119
|
+
components
|
120
|
+
end
|
121
|
+
|
122
|
+
# Return max levels which glob pattern may resolve to
|
123
|
+
#
|
124
|
+
# @api private
|
125
|
+
# @param glob_pattern [String,Regex] pattern to search for
|
126
|
+
# @return [Integer] max levels which pattern may match
|
127
|
+
def self.glob_depth(glob_pattern)
|
128
|
+
path_components = Pathname(glob_pattern).each_filename.to_a
|
129
|
+
return nil if path_components.include?('**')
|
130
|
+
|
131
|
+
path_components.length
|
132
|
+
end
|
133
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'time'
|
2
2
|
require 'metadata/util/win32/peheader'
|
3
3
|
require 'metadata/util/win32/versioninfo'
|
4
|
+
require 'metadata/util/find_class_methods'
|
4
5
|
require 'util/miq-xml'
|
5
6
|
require 'ostruct'
|
6
7
|
require 'util/miq-encode'
|
@@ -54,29 +55,35 @@ class MD5deep
|
|
54
55
|
def scan_glob(filename)
|
55
56
|
filename.tr!("\\", "/")
|
56
57
|
startDir = File.dirname(filename)
|
57
|
-
globPattern = File.basename(filename)
|
58
58
|
@xml.root.add_attribute("base_path", startDir)
|
59
|
-
|
59
|
+
path_prefix = startDir[0, 2]
|
60
|
+
@drive_letter = path_prefix.match?(/^\w\:/) ? path_prefix : ""
|
60
61
|
|
61
62
|
# First check if we are passed a fully qualifed file name
|
62
63
|
if @fs.fileExists?(filename)
|
63
|
-
|
64
|
+
base_file = File.basename(filename)
|
65
|
+
isDir?(filename) ? process_dir_as_file(startDir, base_file, @xml.root) : processFile(startDir, base_file, @xml.root)
|
64
66
|
else
|
65
67
|
# If the file is not found then process the data as a glob pattern.
|
66
|
-
|
67
|
-
# $log.info "Glob file found: [#{f}]"
|
68
|
-
# Passing "startDir" as the first parameter is a work-around for issues
|
69
|
-
# when scanning Win VMs from Linux where the path returned from dirGlob
|
70
|
-
# do not include the drive letter.
|
71
|
-
# Below is the original line
|
72
|
-
# processFile(File.dirname(f), File.basename(f), @xml.root)
|
73
|
-
processFile(startDir, File.basename(f), @xml.root)
|
74
|
-
end
|
68
|
+
process_each_glob_file(filename)
|
75
69
|
end
|
76
70
|
@xml
|
77
71
|
end
|
78
72
|
|
73
|
+
def process_each_glob_file(file_name)
|
74
|
+
FindClassMethods.glob(file_name, @fs) do |f|
|
75
|
+
# Passing "startDir" as the first parameter is a work-around for issues
|
76
|
+
# when scanning Win VMs from Linux where the path returned from dirGlob
|
77
|
+
# do not include the drive letter.
|
78
|
+
processFile(File.dirname(f), File.basename(f), @xml.root)
|
79
|
+
end
|
80
|
+
rescue => err
|
81
|
+
$log.error "process_each_glob_file: Exception #{err} rescued"
|
82
|
+
$log.debug err.backtrace.join("\n")
|
83
|
+
end
|
84
|
+
|
79
85
|
def read_fs(path, xmlNode)
|
86
|
+
@drive_letter = @drive_letter.nil? ? "" : @drive_letter
|
80
87
|
if @fs
|
81
88
|
@fs.dirForeach(path) { |x| processFile(path, x, xmlNode) }
|
82
89
|
@fs.dirForeach(path) { |x| processDir(path, x, xmlNode) }
|
@@ -91,7 +98,7 @@ class MD5deep
|
|
91
98
|
|
92
99
|
def processDir(path, x, xmlNode)
|
93
100
|
if x != "." && x != ".."
|
94
|
-
currFile = File.join(path, x)
|
101
|
+
currFile = File.join(@drive_letter, path, x)
|
95
102
|
|
96
103
|
begin
|
97
104
|
if File.directory?(currFile)
|
@@ -110,7 +117,7 @@ class MD5deep
|
|
110
117
|
|
111
118
|
def process_dir_as_file(path, x, xml_node)
|
112
119
|
if x != "." && x != ".."
|
113
|
-
curr_dir = File.join(path, x)
|
120
|
+
curr_dir = File.join(@drive_letter, path, x)
|
114
121
|
if isDir?(curr_dir)
|
115
122
|
xml_file_node = xml_node.add_element("file", "name" => x, "fqname" => curr_dir)
|
116
123
|
stat_hash = {}
|
@@ -122,39 +129,43 @@ class MD5deep
|
|
122
129
|
|
123
130
|
def processFile(path, x, xmlNode)
|
124
131
|
if (@opts.exclude.include?(x) == false) && x[0..0] != "$"
|
125
|
-
currFile = File.join(path, x)
|
132
|
+
currFile = File.join(@drive_letter, path, x)
|
126
133
|
|
127
134
|
begin
|
128
135
|
# unless File.directory?(currFile) then
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
xmlFileNode.add_element("versioninfo", peHdr.versioninfo) if @opts.versioninfo && !peHdr.versioninfo.blank?
|
146
|
-
xmlFileNode.add_element("libraries", "imports" => peHdr.getImportList) if @opts.imports && !peHdr.imports.blank?
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
getFileContents(fh, xmlFileNode) if @opts.contents == true
|
152
|
-
fh.close
|
136
|
+
return if isDir?(currFile)
|
137
|
+
|
138
|
+
# File we have an exclusion list and the current file is in it, skip to the next file
|
139
|
+
@fullFileCount += 1
|
140
|
+
fh = fileOpen(currFile)
|
141
|
+
|
142
|
+
xmlFileNode = xmlNode.add_element("file", "name" => x, "fqname" => currFile)
|
143
|
+
statHash = {}
|
144
|
+
statHash.merge!(getFileStats(fh))
|
145
|
+
statHash.merge!(calculate_digest(fh))
|
146
|
+
xmlFileNode.add_attributes(statHash)
|
147
|
+
|
148
|
+
ext = File.extname(currFile).downcase
|
149
|
+
if @opts.winVerList.include?(ext)
|
150
|
+
pe_hdr = PEheader.new(fh) rescue nil
|
151
|
+
process_pe_header(pe_hdr, xmlFileNode) unless pe_hdr.nil?
|
153
152
|
end
|
153
|
+
|
154
|
+
getFileContents(fh, xmlFileNode) if @opts.contents == true
|
155
|
+
fh.close
|
154
156
|
rescue Errno::EACCES, RuntimeError, SystemCallError
|
155
157
|
fh.close if fh.kind_of?(File) && !fh.closed?
|
156
158
|
end
|
157
159
|
end
|
160
|
+
$log.debug "processFile: finished @xml is #{@xml}"
|
161
|
+
end
|
162
|
+
|
163
|
+
def process_pe_header(pe_hdr, xml_file_node)
|
164
|
+
xml_file_node.add_element("versioninfo", pe_hdr.versioninfo) if @opts.versioninfo && pe_hdr.versioninfo.present?
|
165
|
+
xml_file_node.add_element("libraries", "imports" => pe_hdr.getImportList) if @opts.imports && pe_hdr.imports.present?
|
166
|
+
rescue TypeError => err
|
167
|
+
$log.info "process_pe_header: TypeError handling PEheader; skipping PEheader info"
|
168
|
+
$log.debug err.backtrace.join("\n")
|
158
169
|
end
|
159
170
|
|
160
171
|
def isDir?(currFile)
|
@@ -215,10 +226,15 @@ class MD5deep
|
|
215
226
|
# Create hash for requested digests
|
216
227
|
digest = create_digest_hash
|
217
228
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
229
|
+
begin
|
230
|
+
fileName.seek(0, IO::SEEK_SET)
|
231
|
+
# Loop over each digest and add the file contents
|
232
|
+
while (buf = fileName.read(10_240_000))
|
233
|
+
digest.each_pair { |_k, v| v << buf }
|
234
|
+
end
|
235
|
+
rescue => err
|
236
|
+
$log.error "Error #{err} reading file to calculate digest"
|
237
|
+
$log.debug err.backtrace.join("\n")
|
222
238
|
end
|
223
239
|
end
|
224
240
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manageiq-smartstate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ManageIQ Developers
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: azure-armrest
|
@@ -263,7 +263,7 @@ dependencies:
|
|
263
263
|
- !ruby/object:Gem::Version
|
264
264
|
version: 1.0.0
|
265
265
|
description: ManageIQ SmartState Analysis
|
266
|
-
email:
|
266
|
+
email:
|
267
267
|
executables: []
|
268
268
|
extensions: []
|
269
269
|
extra_rdoc_files: []
|
@@ -525,6 +525,7 @@ files:
|
|
525
525
|
- lib/metadata/linux/MiqConaryPackages.rb
|
526
526
|
- lib/metadata/linux/MiqRpmPackages.rb
|
527
527
|
- lib/metadata/util/event_log_filter.rb
|
528
|
+
- lib/metadata/util/find_class_methods.rb
|
528
529
|
- lib/metadata/util/md5deep.rb
|
529
530
|
- lib/metadata/util/win32/Win32Accounts.rb
|
530
531
|
- lib/metadata/util/win32/Win32EventLog.rb
|
@@ -544,7 +545,7 @@ homepage: https://github.com/ManageIQ/manageiq-smartstate
|
|
544
545
|
licenses:
|
545
546
|
- Apache-2.0
|
546
547
|
metadata: {}
|
547
|
-
post_install_message:
|
548
|
+
post_install_message:
|
548
549
|
rdoc_options: []
|
549
550
|
require_paths:
|
550
551
|
- lib
|
@@ -559,9 +560,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
559
560
|
- !ruby/object:Gem::Version
|
560
561
|
version: '0'
|
561
562
|
requirements: []
|
562
|
-
|
563
|
-
|
564
|
-
signing_key:
|
563
|
+
rubygems_version: 3.0.3
|
564
|
+
signing_key:
|
565
565
|
specification_version: 4
|
566
566
|
summary: ManageIQ SmartState Analysis
|
567
567
|
test_files: []
|