manageiq-smartstate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (305) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +4 -0
  4. data/.rspec_ci +4 -0
  5. data/.travis.yml +15 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE.txt +202 -0
  8. data/README.md +45 -0
  9. data/Rakefile +23 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/MiqContainerGroup/MiqContainerGroup.rb +31 -0
  13. data/lib/MiqVm/MiqLocalVm.rb +50 -0
  14. data/lib/MiqVm/MiqRhevmVm.rb +179 -0
  15. data/lib/MiqVm/MiqVm.rb +355 -0
  16. data/lib/MiqVm/miq_azure_vm.rb +96 -0
  17. data/lib/MiqVm/miq_scvmm_vm.rb +38 -0
  18. data/lib/MiqVm/test/camcorder_fleece_test.rb +60 -0
  19. data/lib/MiqVm/test/localVm.rb +45 -0
  20. data/lib/MiqVm/test/partitionAlignmentCheck.rb +76 -0
  21. data/lib/MiqVm/test/remoteVm.rb +65 -0
  22. data/lib/MiqVm/test/rhevmNfsTest.rb +62 -0
  23. data/lib/MiqVm/test/rhevmNfsTest2.rb +66 -0
  24. data/lib/MiqVm/test/rhevmTest.rb +70 -0
  25. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackCommon.rb +107 -0
  26. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackImage.rb +67 -0
  27. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackInstance.rb +182 -0
  28. data/lib/Scvmm/miq_hyperv_disk.rb +273 -0
  29. data/lib/Scvmm/miq_scvmm_parse_powershell.rb +75 -0
  30. data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +135 -0
  31. data/lib/Scvmm/test/miq_hyperv_disk_test.rb +33 -0
  32. data/lib/Scvmm/test/miq_scvmm_vm_ssa_info_test.rb +41 -0
  33. data/lib/VmLocalDiskAccess/test/localCfg.rb +97 -0
  34. data/lib/VolumeManager/LVM/logical_volume.rb +75 -0
  35. data/lib/VolumeManager/LVM/lv_segment.rb +43 -0
  36. data/lib/VolumeManager/LVM/lvm2disk.rb +158 -0
  37. data/lib/VolumeManager/LVM/parser.rb +138 -0
  38. data/lib/VolumeManager/LVM/physical_volume.rb +19 -0
  39. data/lib/VolumeManager/LVM/scanner.rb +156 -0
  40. data/lib/VolumeManager/LVM/thin/btree.rb +83 -0
  41. data/lib/VolumeManager/LVM/thin/constants.rb +86 -0
  42. data/lib/VolumeManager/LVM/thin/data_map.rb +44 -0
  43. data/lib/VolumeManager/LVM/thin/mapping_tree.rb +19 -0
  44. data/lib/VolumeManager/LVM/thin/space_maps.rb +58 -0
  45. data/lib/VolumeManager/LVM/thin/superblock.rb +136 -0
  46. data/lib/VolumeManager/LVM/thin.rb +6 -0
  47. data/lib/VolumeManager/LVM/volume_group.rb +97 -0
  48. data/lib/VolumeManager/LVM.rb +8 -0
  49. data/lib/VolumeManager/MiqLdm.rb +546 -0
  50. data/lib/VolumeManager/MiqLvm.rb +17 -0
  51. data/lib/VolumeManager/MiqNativeVolumeManager.rb +150 -0
  52. data/lib/VolumeManager/MiqVolumeManager.rb +277 -0
  53. data/lib/VolumeManager/VolMgrPlatformSupport.rb +18 -0
  54. data/lib/VolumeManager/VolMgrPlatformSupportLinux.rb +77 -0
  55. data/lib/VolumeManager/VolMgrPlatformSupportWin.rb +17 -0
  56. data/lib/VolumeManager/test/blockDevTest.rb +40 -0
  57. data/lib/VolumeManager/test/ldm.rb +97 -0
  58. data/lib/blackbox/VmBlackBox.rb +103 -0
  59. data/lib/blackbox/xmlStorage.rb +180 -0
  60. data/lib/db/MiqBdb/MiqBdb.rb +309 -0
  61. data/lib/db/MiqBdb/MiqBdbBtree.rb +219 -0
  62. data/lib/db/MiqBdb/MiqBdbHash.rb +199 -0
  63. data/lib/db/MiqBdb/MiqBdbPage.rb +159 -0
  64. data/lib/db/MiqBdb/MiqBdbUtil.rb +18 -0
  65. data/lib/db/MiqSqlite/MiqSqlite3.rb +330 -0
  66. data/lib/db/MiqSqlite/MiqSqlite3Cell.rb +167 -0
  67. data/lib/db/MiqSqlite/MiqSqlite3Page.rb +151 -0
  68. data/lib/db/MiqSqlite/MiqSqlite3Table.rb +124 -0
  69. data/lib/db/MiqSqlite/MiqSqlite3Util.rb +32 -0
  70. data/lib/disk/DiskProbe.rb +68 -0
  71. data/lib/disk/MiqDisk.rb +317 -0
  72. data/lib/disk/camcorder_test.rb +90 -0
  73. data/lib/disk/dos_mbr.img +0 -0
  74. data/lib/disk/modules/AzureBlobDisk.rb +101 -0
  75. data/lib/disk/modules/LocalDevMod.rb +47 -0
  76. data/lib/disk/modules/LocalDevProbe.rb +6 -0
  77. data/lib/disk/modules/MSCommon.rb +352 -0
  78. data/lib/disk/modules/MSVSDiffDisk.rb +91 -0
  79. data/lib/disk/modules/MSVSDiskProbe.rb +61 -0
  80. data/lib/disk/modules/MSVSDynamicDisk.rb +42 -0
  81. data/lib/disk/modules/MSVSFixedDisk.rb +45 -0
  82. data/lib/disk/modules/MiqLargeFile.rb +63 -0
  83. data/lib/disk/modules/MiqLargeFileWin32.rb +107 -0
  84. data/lib/disk/modules/QcowDisk.rb +692 -0
  85. data/lib/disk/modules/QcowDiskProbe.rb +34 -0
  86. data/lib/disk/modules/RawBlockIO.rb +116 -0
  87. data/lib/disk/modules/RawDisk.rb +45 -0
  88. data/lib/disk/modules/RawDiskProbe.rb +7 -0
  89. data/lib/disk/modules/RhevmDescriptor.rb +167 -0
  90. data/lib/disk/modules/RhevmDiskProbe.rb +52 -0
  91. data/lib/disk/modules/VMWareCowdDisk.rb +207 -0
  92. data/lib/disk/modules/VMWareDescriptor.rb +214 -0
  93. data/lib/disk/modules/VMWareDiskProbe.rb +74 -0
  94. data/lib/disk/modules/VMWareSparseDisk.rb +189 -0
  95. data/lib/disk/modules/VhdxDisk.rb +625 -0
  96. data/lib/disk/modules/VhdxDiskProbe.rb +46 -0
  97. data/lib/disk/modules/VixDiskMod.rb +54 -0
  98. data/lib/disk/modules/VixDiskProbe.rb +6 -0
  99. data/lib/disk/modules/miq_disk_cache.rb +135 -0
  100. data/lib/disk/modules/miq_dummy_disk.rb +41 -0
  101. data/lib/disk/modules/vhdx_bat_entry.rb +10 -0
  102. data/lib/disk/test.rb +66 -0
  103. data/lib/fs/MetakitFS/MetakitFS.rb +530 -0
  104. data/lib/fs/MetakitFS/test/Makefile +14 -0
  105. data/lib/fs/MetakitFS/test/MkCollectFiles.rb +165 -0
  106. data/lib/fs/MetakitFS/test/MkSelectFiles.rb +30 -0
  107. data/lib/fs/MetakitFS/test/collect_files.yaml +70 -0
  108. data/lib/fs/MetakitFS/test/init.rb +3 -0
  109. data/lib/fs/MetakitFS/test/mk2vmdk.rb +64 -0
  110. data/lib/fs/MetakitFS/test/mk4test.c +92 -0
  111. data/lib/fs/MetakitFS/test/mkFsTest.rb +113 -0
  112. data/lib/fs/MetakitFS/test/proto.rb +97 -0
  113. data/lib/fs/MiqFS/FsProbe.rb +39 -0
  114. data/lib/fs/MiqFS/MiqFS.rb +515 -0
  115. data/lib/fs/MiqFS/modules/AUFSProbe.rb +26 -0
  116. data/lib/fs/MiqFS/modules/Ext3.rb +305 -0
  117. data/lib/fs/MiqFS/modules/Ext3Probe.rb +25 -0
  118. data/lib/fs/MiqFS/modules/Ext4.rb +304 -0
  119. data/lib/fs/MiqFS/modules/Ext4Probe.rb +25 -0
  120. data/lib/fs/MiqFS/modules/Fat32.rb +318 -0
  121. data/lib/fs/MiqFS/modules/Fat32Probe.rb +30 -0
  122. data/lib/fs/MiqFS/modules/HFSProbe.rb +18 -0
  123. data/lib/fs/MiqFS/modules/Iso9660.rb +293 -0
  124. data/lib/fs/MiqFS/modules/Iso9660Probe.rb +18 -0
  125. data/lib/fs/MiqFS/modules/LocalFS.rb +105 -0
  126. data/lib/fs/MiqFS/modules/NTFS.rb +287 -0
  127. data/lib/fs/MiqFS/modules/NTFSProbe.rb +21 -0
  128. data/lib/fs/MiqFS/modules/NativeFS.rb +155 -0
  129. data/lib/fs/MiqFS/modules/ReFSProbe.rb +17 -0
  130. data/lib/fs/MiqFS/modules/RealFS.rb +79 -0
  131. data/lib/fs/MiqFS/modules/RealFSProbe.rb +6 -0
  132. data/lib/fs/MiqFS/modules/Reiser4Probe.rb +18 -0
  133. data/lib/fs/MiqFS/modules/ReiserFS.rb +315 -0
  134. data/lib/fs/MiqFS/modules/ReiserFSProbe.rb +42 -0
  135. data/lib/fs/MiqFS/modules/UnionFSProbe.rb +18 -0
  136. data/lib/fs/MiqFS/modules/WebDAV.rb +127 -0
  137. data/lib/fs/MiqFS/modules/WebDAVFile.rb +68 -0
  138. data/lib/fs/MiqFS/modules/XFS.rb +300 -0
  139. data/lib/fs/MiqFS/modules/XFSProbe.rb +26 -0
  140. data/lib/fs/MiqFS/modules/ZFSProbe.rb +18 -0
  141. data/lib/fs/MiqFS/test.rb +59 -0
  142. data/lib/fs/MiqFsUtil.rb +383 -0
  143. data/lib/fs/MiqMountManager.rb +209 -0
  144. data/lib/fs/MiqNativeMountManager.rb +101 -0
  145. data/lib/fs/MountManagerProbe.rb +29 -0
  146. data/lib/fs/ReiserFS/block.rb +209 -0
  147. data/lib/fs/ReiserFS/directory.rb +136 -0
  148. data/lib/fs/ReiserFS/directory_entry.rb +140 -0
  149. data/lib/fs/ReiserFS/file_data.rb +111 -0
  150. data/lib/fs/ReiserFS/superblock.rb +140 -0
  151. data/lib/fs/ReiserFS/utils.rb +95 -0
  152. data/lib/fs/VimDatastoreFS/VimDatastoreFS.rb +192 -0
  153. data/lib/fs/ext3/alloc_bitmap.rb +38 -0
  154. data/lib/fs/ext3/block_pointers_path.rb +130 -0
  155. data/lib/fs/ext3/directory.rb +51 -0
  156. data/lib/fs/ext3/directory_entry.rb +67 -0
  157. data/lib/fs/ext3/ex_attrib_header.rb +14 -0
  158. data/lib/fs/ext3/ex_attrib_name.rb +23 -0
  159. data/lib/fs/ext3/file_data.rb +130 -0
  160. data/lib/fs/ext3/group_descriptor_entry.rb +65 -0
  161. data/lib/fs/ext3/group_descriptor_table.rb +54 -0
  162. data/lib/fs/ext3/hash_tree_entry.rb +18 -0
  163. data/lib/fs/ext3/hash_tree_header.rb +15 -0
  164. data/lib/fs/ext3/inode.rb +228 -0
  165. data/lib/fs/ext3/posix_acl_entry.rb +29 -0
  166. data/lib/fs/ext3/posix_acl_header.rb +11 -0
  167. data/lib/fs/ext3/superblock.rb +406 -0
  168. data/lib/fs/ext3/test/tc_Ext3BlockPointersPath.rb +74 -0
  169. data/lib/fs/ext4/alloc_bitmap.rb +38 -0
  170. data/lib/fs/ext4/directory.rb +87 -0
  171. data/lib/fs/ext4/directory_entry.rb +77 -0
  172. data/lib/fs/ext4/ex_attrib_header.rb +14 -0
  173. data/lib/fs/ext4/ex_attrib_name.rb +23 -0
  174. data/lib/fs/ext4/extent.rb +35 -0
  175. data/lib/fs/ext4/extent_header.rb +40 -0
  176. data/lib/fs/ext4/extent_index.rb +33 -0
  177. data/lib/fs/ext4/group_descriptor_entry.rb +69 -0
  178. data/lib/fs/ext4/group_descriptor_table.rb +54 -0
  179. data/lib/fs/ext4/hash_tree_entry.rb +58 -0
  180. data/lib/fs/ext4/hash_tree_header.rb +35 -0
  181. data/lib/fs/ext4/inode.rb +465 -0
  182. data/lib/fs/ext4/posix_acl_entry.rb +29 -0
  183. data/lib/fs/ext4/posix_acl_header.rb +11 -0
  184. data/lib/fs/ext4/superblock.rb +412 -0
  185. data/lib/fs/fat32/boot_sect.rb +379 -0
  186. data/lib/fs/fat32/directory.rb +222 -0
  187. data/lib/fs/fat32/directory_entry.rb +540 -0
  188. data/lib/fs/fat32/file_data.rb +128 -0
  189. data/lib/fs/iso9660/boot_sector.rb +170 -0
  190. data/lib/fs/iso9660/directory.rb +90 -0
  191. data/lib/fs/iso9660/directory_entry.rb +147 -0
  192. data/lib/fs/iso9660/file_data.rb +78 -0
  193. data/lib/fs/iso9660/rock_ridge.rb +329 -0
  194. data/lib/fs/iso9660/util.rb +57 -0
  195. data/lib/fs/modules/LinuxMount.rb +300 -0
  196. data/lib/fs/modules/LinuxMountProbe.rb +29 -0
  197. data/lib/fs/modules/WinMount.rb +97 -0
  198. data/lib/fs/modules/WinMountProbe.rb +24 -0
  199. data/lib/fs/ntfs/attrib_attribute_list.rb +131 -0
  200. data/lib/fs/ntfs/attrib_bitmap.rb +26 -0
  201. data/lib/fs/ntfs/attrib_data.rb +74 -0
  202. data/lib/fs/ntfs/attrib_file_name.rb +110 -0
  203. data/lib/fs/ntfs/attrib_header.rb +194 -0
  204. data/lib/fs/ntfs/attrib_index_allocation.rb +19 -0
  205. data/lib/fs/ntfs/attrib_index_root.rb +247 -0
  206. data/lib/fs/ntfs/attrib_object_id.rb +40 -0
  207. data/lib/fs/ntfs/attrib_standard_information.rb +107 -0
  208. data/lib/fs/ntfs/attrib_type.rb +49 -0
  209. data/lib/fs/ntfs/attrib_volume_information.rb +53 -0
  210. data/lib/fs/ntfs/attrib_volume_name.rb +31 -0
  211. data/lib/fs/ntfs/boot_sect.rb +253 -0
  212. data/lib/fs/ntfs/data_run.rb +358 -0
  213. data/lib/fs/ntfs/directory_index_node.rb +114 -0
  214. data/lib/fs/ntfs/index_node_header.rb +69 -0
  215. data/lib/fs/ntfs/index_record_header.rb +85 -0
  216. data/lib/fs/ntfs/mft_entry.rb +288 -0
  217. data/lib/fs/ntfs/utils.rb +43 -0
  218. data/lib/fs/test/camcorder_fs_test.rb +108 -0
  219. data/lib/fs/test/collect_files_direct.yaml +22 -0
  220. data/lib/fs/test/collect_files_in.yaml +24 -0
  221. data/lib/fs/test/collect_files_in_nc.yaml +22 -0
  222. data/lib/fs/test/collect_files_out.yaml +6 -0
  223. data/lib/fs/test/collect_files_rm.yaml +6 -0
  224. data/lib/fs/test/copyTest.rb +126 -0
  225. data/lib/fs/test/fsTest.rb +87 -0
  226. data/lib/fs/test/updateTest.rb +184 -0
  227. data/lib/fs/xfs/allocation_group.rb +160 -0
  228. data/lib/fs/xfs/bmap_btree_block.rb +125 -0
  229. data/lib/fs/xfs/bmap_btree_record.rb +80 -0
  230. data/lib/fs/xfs/bmap_btree_root_node.rb +72 -0
  231. data/lib/fs/xfs/directory.rb +133 -0
  232. data/lib/fs/xfs/directory2_data_header.rb +27 -0
  233. data/lib/fs/xfs/directory3_data_header.rb +34 -0
  234. data/lib/fs/xfs/directory_block_tail.rb +22 -0
  235. data/lib/fs/xfs/directory_data_header.rb +46 -0
  236. data/lib/fs/xfs/directory_entry.rb +106 -0
  237. data/lib/fs/xfs/inode.rb +532 -0
  238. data/lib/fs/xfs/inode_map.rb +100 -0
  239. data/lib/fs/xfs/short_form_directory_entry.rb +91 -0
  240. data/lib/fs/xfs/short_form_header.rb +44 -0
  241. data/lib/fs/xfs/superblock.rb +556 -0
  242. data/lib/lib/tasks/azure.rake +52 -0
  243. data/lib/manageiq/smartstate/version.rb +5 -0
  244. data/lib/manageiq/smartstate.rb +7 -0
  245. data/lib/manageiq-smartstate.rb +1 -0
  246. data/lib/metadata/MIQExtract/MIQExtract.rb +297 -0
  247. data/lib/metadata/MIQExtract/test/extractTest.rb +41 -0
  248. data/lib/metadata/MIQExtract/test/full_extract_test.rb +68 -0
  249. data/lib/metadata/ScanProfile/HostScanItem.rb +4 -0
  250. data/lib/metadata/ScanProfile/HostScanProfile.rb +4 -0
  251. data/lib/metadata/ScanProfile/HostScanProfiles.rb +41 -0
  252. data/lib/metadata/ScanProfile/ScanItemBase.rb +63 -0
  253. data/lib/metadata/ScanProfile/ScanProfileBase.rb +51 -0
  254. data/lib/metadata/ScanProfile/ScanProfilesBase.rb +60 -0
  255. data/lib/metadata/ScanProfile/VmScanItem.rb +4 -0
  256. data/lib/metadata/ScanProfile/VmScanProfile.rb +4 -0
  257. data/lib/metadata/ScanProfile/VmScanProfiles.rb +38 -0
  258. data/lib/metadata/ScanProfile/modules/HostScanItemFile.rb +51 -0
  259. data/lib/metadata/ScanProfile/modules/HostScanItemNteventlog.rb +84 -0
  260. data/lib/metadata/ScanProfile/modules/VmScanItemFile.rb +39 -0
  261. data/lib/metadata/ScanProfile/modules/VmScanItemNteventlog.rb +34 -0
  262. data/lib/metadata/ScanProfile/modules/VmScanItemRegistry.rb +64 -0
  263. data/lib/metadata/VMMount/VMMount.rb +81 -0
  264. data/lib/metadata/VMMount/VMPlatformMount.rb +18 -0
  265. data/lib/metadata/VMMount/VMPlatformMountLinux.rb +75 -0
  266. data/lib/metadata/VMMount/VMPlatformMountWin.rb +13 -0
  267. data/lib/metadata/VmConfig/GetNativeCfg.rb +45 -0
  268. data/lib/metadata/VmConfig/VmConfig.rb +947 -0
  269. data/lib/metadata/VmConfig/cfgConfig.rb +45 -0
  270. data/lib/metadata/VmConfig/ovfConfig.rb +99 -0
  271. data/lib/metadata/VmConfig/test/GetVMwareCfgTest.rb +40 -0
  272. data/lib/metadata/VmConfig/vmcConfig.rb +116 -0
  273. data/lib/metadata/VmConfig/vmtxConfig.rb +4 -0
  274. data/lib/metadata/VmConfig/vmxConfig.rb +162 -0
  275. data/lib/metadata/VmConfig/xmlConfig.rb +79 -0
  276. data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +41 -0
  277. data/lib/metadata/linux/InitProcHash.rb +632 -0
  278. data/lib/metadata/linux/LinuxInitProcs.rb +142 -0
  279. data/lib/metadata/linux/LinuxOSInfo.rb +237 -0
  280. data/lib/metadata/linux/LinuxPackages.rb +209 -0
  281. data/lib/metadata/linux/LinuxSystemd.rb +130 -0
  282. data/lib/metadata/linux/LinuxUsers.rb +289 -0
  283. data/lib/metadata/linux/LinuxUtils.rb +197 -0
  284. data/lib/metadata/linux/MiqConaryPackages.rb +41 -0
  285. data/lib/metadata/linux/MiqRpmPackages.rb +160 -0
  286. data/lib/metadata/linux/test/Name +0 -0
  287. data/lib/metadata/linux/test/Packages +0 -0
  288. data/lib/metadata/linux/test/rpoTest.rb +5 -0
  289. data/lib/metadata/linux/test/tc_LinuxUtils.rb +4157 -0
  290. data/lib/metadata/util/event_log_filter.rb +61 -0
  291. data/lib/metadata/util/md5deep.rb +280 -0
  292. data/lib/metadata/util/win32/Win32Accounts.rb +764 -0
  293. data/lib/metadata/util/win32/Win32EventLog.rb +743 -0
  294. data/lib/metadata/util/win32/Win32Services.rb +86 -0
  295. data/lib/metadata/util/win32/Win32Software.rb +326 -0
  296. data/lib/metadata/util/win32/Win32System.rb +333 -0
  297. data/lib/metadata/util/win32/boot_info_win.rb +59 -0
  298. data/lib/metadata/util/win32/fleece_hives.rb +220 -0
  299. data/lib/metadata/util/win32/ms-registry.rb +650 -0
  300. data/lib/metadata/util/win32/peheader.rb +868 -0
  301. data/lib/metadata/util/win32/remote-registry.rb +142 -0
  302. data/lib/metadata/util/win32/system_path_win.rb +103 -0
  303. data/lib/metadata/util/win32/versioninfo.rb +17 -0
  304. data/manageiq-smartstate.gemspec +35 -0
  305. metadata +486 -0
@@ -0,0 +1,947 @@
1
+ require 'pathname'
2
+ require 'metadata/VMMount/VMMount'
3
+ require 'util/miq-unicode'
4
+ require 'util/miq-xml'
5
+ require 'VMwareWebService/MiqVimInventory'
6
+ require 'timeout'
7
+ require 'util/miq-extensions'
8
+ require 'VMwareWebService/MiqVimBroker'
9
+
10
+ class VmConfig
11
+ attr_reader :configFile
12
+
13
+ def initialize(filename)
14
+ @configFile = nil
15
+ @configPath = nil
16
+ @direct_file_access = true
17
+
18
+ if filename.kind_of? Hash
19
+ @direct_file_access = false
20
+ @cfgHash = filename
21
+ else
22
+ @cfgHash = {}
23
+ # If filename contains embedded new-line chars treat it as data. Otherwise, its a filename.
24
+ if filename.index("\n")
25
+ f = filename
26
+ else
27
+ set_vmconfig_path(filename)
28
+
29
+ begin
30
+ configType = File.extname(filename).delete(".").downcase
31
+ require "metadata/VmConfig/#{configType}Config"
32
+ rescue LoadError => e
33
+ raise e, "Filetype unrecognized for file #{filename}"
34
+ end
35
+ extend Kernel.const_get(configType.capitalize + "Config")
36
+ f = convert(filename)
37
+ end
38
+
39
+ process_file(f)
40
+
41
+ f.close if f.class == File
42
+
43
+ postProcessDisks
44
+ end
45
+
46
+ # Handle oddities
47
+ configuration_fixup
48
+ end
49
+
50
+ def process_file(data_lines)
51
+ data_lines.each_line do |line|
52
+ line.AsciiToUtf8!.strip!
53
+ next if line.length == 0
54
+ next if line =~ /^#.*$/
55
+ next unless line.include?("=")
56
+ k, v = line.split(/\s*=\s*/)
57
+
58
+ # Note: All key names are lower-cased for easy lookup
59
+ k = k.downcase
60
+ v = v.gsub(/^"/, "").gsub(/"$/, "")
61
+
62
+ @cfgHash[k] = v
63
+ end
64
+ end
65
+
66
+ def postProcessDisks
67
+ # Convert absolute paths to relative paths for the disks
68
+ if @direct_file_access
69
+ getAllDiskKeys.each do |dk|
70
+ next if @cfgHash[dk + ".devicetype"] && @cfgHash[dk + ".devicetype"].include?("cdrom")
71
+ next if @cfgHash[dk + ".filename"] && @cfgHash[dk + ".filename"].downcase == "auto detect"
72
+
73
+ begin
74
+ dskPath = Pathname.new(@cfgHash[dk + ".filename"])
75
+ begin
76
+ @cfgHash[dk + ".filename"] = dskPath.relative_path_from(Pathname.new(@configPath)).to_s.tr("\\", "/") if dskPath.absolute?
77
+ rescue
78
+ @cfgHash[dk + ".filename"] = dskPath.to_s.tr("\\", "/")
79
+ end
80
+ if self.respond_to?(:diskCreateType)
81
+ createType = diskCreateType(@cfgHash[dk + ".filename"])
82
+ @cfgHash[dk + ".createType"] = createType if createType
83
+ end
84
+ if self.respond_to?(:diskControllerType)
85
+ adapterType = diskControllerType(@cfgHash[dk + ".filename"])
86
+ @cfgHash[dk + ".adapterType"] = adapterType if adapterType
87
+ end
88
+ rescue => err
89
+ $log.warn "VmConfig: Failed to convert path: #{@cfgHash[dk + ".filename"]}, #{err}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def getHash
96
+ @cfgHash
97
+ end
98
+
99
+ def getDiskFileHash
100
+ return @diskFileHash if @diskFileHash
101
+
102
+ @diskFileHash = {}
103
+ getAllDiskKeys.each do |dk|
104
+ next if @cfgHash[dk + ".devicetype"] && @cfgHash[dk + ".devicetype"].include?("cdrom")
105
+ next if @cfgHash[dk + ".filename"] && @cfgHash[dk + ".filename"].downcase == "auto detect"
106
+ filename = @cfgHash[dk + ".filename"]
107
+ @diskFileHash[dk] = filename
108
+ if @direct_file_access
109
+ ds, _dir, _name = split_filename(filename)
110
+ if ds.nil? && !Pathname.new(filename).absolute?
111
+ @diskFileHash[dk] = File.expand_path(File.join(@configPath, filename))
112
+ end
113
+ end
114
+ end
115
+ @diskFileHash
116
+ end # getDiskFileHash
117
+
118
+ def getAllDiskKeys
119
+ dskKeys = @cfgHash.keys.delete_if { |e| !diskKey?(e) }.collect! { |e| e.gsub(".filename", "") }
120
+ dskKeys.delete_if { |dk| @cfgHash[dk + ".present"].to_s.downcase == "false" }
121
+ dskKeys.sort
122
+ end
123
+
124
+ def getSnapshotDiskFileHash
125
+ return @snapshotdiskFileHash if @snapshotdiskFileHash
126
+
127
+ # For snapshots we need to keep the disks order by snapshot uid, then by device
128
+ @snapshotdiskFileHash = Hash.new { |h, k| h[k] = {} }
129
+ getSnapshotDiskKeys.each do |dk|
130
+ next if @cfgHash[dk + ".devicetype"].to_s.include?("cdrom")
131
+ next if @cfgHash[dk + ".filename"].to_s.downcase == "auto detect"
132
+ dk =~ /^snapshot\d+/
133
+ sn_uid = @cfgHash["#{$&}.uid"]
134
+ key = @cfgHash[dk + ".node"]
135
+
136
+ # Independent disks do not par-take in snapshots. Check the mode of the parent disk.
137
+ next if @cfgHash["#{key}.mode"].to_s.include?('independent')
138
+
139
+ @snapshotdiskFileHash[sn_uid]['disks'] ||= {}
140
+ filename = @cfgHash[dk + ".filename"]
141
+ disk_path = filename
142
+
143
+ if @direct_file_access
144
+ ds, _dir, _name = split_filename(filename)
145
+ if ds.nil? && !Pathname.new(@cfgHash[dk + ".filename"]).absolute?
146
+ disk_path = File.join(@configPath, filename)
147
+ end
148
+ end
149
+ @snapshotdiskFileHash[sn_uid]['disks'][key] = disk_path
150
+ end
151
+ @snapshotdiskFileHash
152
+ end # getSnapshotDiskFileHash
153
+
154
+ def getSnapshotDiskKeys
155
+ dskKeys = @cfgHash.keys.delete_if { |e| !snapshotdiskKey?(e) }.collect! { |e| e.gsub(".filename", "") }
156
+ dskKeys.delete_if { |dk| @cfgHash[dk + ".present"].to_s.downcase == "false" }
157
+ dskKeys.sort
158
+ end
159
+
160
+ def set_vmconfig_path(config_path)
161
+ ds, _dir, _name = split_filename(config_path)
162
+ @configFile = ds.nil? ? File.normalize(config_path) : config_path
163
+ @configPath = File.dirname(@configFile)
164
+ end
165
+
166
+ def to_xml(diskStats = true, miqvm = nil)
167
+ # If loaded directly from a hash object, try to add snapshot metadata before processing
168
+ load_vim_snapshots(miqvm)
169
+
170
+ set_vmconfig_path(miqvm.vmConfigFile) unless miqvm.nil?
171
+
172
+ normalize_file_paths
173
+
174
+ xml = MiqXml.createDoc("<vm_configuration><hardware></hardware><vm/></vm_configuration>", "vendor" => vendor)
175
+ # Sort the Hash into an array to group like keys and add each array to the XML doc
176
+ @cfgHash.sort { |a, b| a <=> b }.each do |(k, v)|
177
+ next if k[0..0] == '.'
178
+ insert_XML(k, v, xml)
179
+ end
180
+
181
+ # Pre-load data
182
+ unless miqvm.nil?
183
+ # Make sure we have the volume manager and loaded
184
+ begin
185
+ miqvm.rootTrees[0]
186
+ @vol_mgr_loaded = true
187
+ rescue LoadError
188
+ $log.warn "add_disk_stats [#{$!.class}]-[#{$!}]"
189
+ end
190
+
191
+ files(miqvm)
192
+ end
193
+
194
+ # Add virtual disk sizes
195
+ add_disk_stats(xml, miqvm) if diskStats
196
+
197
+ # Here we list all files in the directory and get their size on disk.
198
+ add_file_sizes(xml, miqvm)
199
+
200
+ # Add partition and volume information
201
+ add_volumes(xml, miqvm)
202
+
203
+ # Add date and size info for snapshots
204
+ add_snapshot_size(xml, miqvm)
205
+
206
+ # TODO: Re-enable
207
+ # normalize_path_names(xml)
208
+
209
+ xml
210
+ end
211
+
212
+ alias_method :toXML, :to_xml
213
+
214
+ def getDriveImageList
215
+ vmDisks = []
216
+ # Convert config file to XML
217
+ xml = toXML(false)
218
+ # Find the disk section of the XML
219
+ xml.root.each_recursive do |e|
220
+ # Only process drives that are present and have a filename
221
+ if (!e.attributes['present'].nil? && e.attributes['present'].downcase == 'true') && !e.attributes['filename'].nil? && (!e.attributes['type'].nil? && e.attributes['type'].downcase == 'disk') && (!e.attributes['id'].nil? && e.attributes['id'].include?(":"))
222
+ # Make sure the disk we are looking at is not a CD-ROM
223
+ if e.attributes['devicetype'].nil? || (!e.attributes['devicetype'].nil? && e.attributes['devicetype'].downcase.index("cd").nil?)
224
+ diskName = e.attributes['filename'].tr("\"", "").strip.tr("\\", "/")
225
+ diskName = File.join(@configPath, diskName) unless diskName[0..0] == "/"
226
+ $log.debug "Adding Disk name to list: [#{diskName}]"
227
+ begin
228
+ vmDisks.push(File.expand_path(diskName))
229
+ rescue
230
+ vmDisks.push(diskName)
231
+ end
232
+ $log.debug "Adding Disk name to list: [#{diskName}]"
233
+ # $log.info "vmDisk Array = #{vmDisks.to_s()}"
234
+ end
235
+ end
236
+ end
237
+ vmDisks
238
+ end
239
+
240
+ def dump_config_to_log(log_level = :warn)
241
+ $log.send(log_level, "VmConfig: Start of configuration info for: #{@configFile}")
242
+ @cfgHash.each { |k, v| $log.send(log_level, "#{k} = #{v}") }
243
+ $log.send(log_level, "VmConfig: End of configuration info for: #{@configFile}")
244
+ end
245
+
246
+ def vendor
247
+ "unknown"
248
+ end
249
+
250
+ def find_file(filename, file_list = files)
251
+ # basename = File.basename(filename)
252
+ # return file_list.detect {|f| f[:name] == basename}
253
+ file_list.detect { |f| f[:path] == filename }
254
+ end
255
+
256
+ private
257
+
258
+ def diskKey?(key)
259
+ key =~ /^ide\d+:\d+\.filename$/ || key =~ /^scsi\d+:\d+\.filename$/
260
+ end
261
+
262
+ def snapshotdiskKey?(key)
263
+ key =~ /^snapshot\d+.disk\d+.filename$/
264
+ end
265
+
266
+ def insert_XML(key, value, xml)
267
+ maj, min, other = key.split(".")
268
+
269
+ # Any elements that cannot be split are put into a misc class
270
+ min, maj = maj, maj if min.nil?
271
+
272
+ if maj.index(/\d/)
273
+ maj_name = $`.chomp(':')
274
+ else
275
+ maj_name = maj
276
+ end
277
+ if min[0...3] == "mem" || min[0...5] == "nvram"
278
+ path = [["hardware"], ["memory"]]
279
+ elsif maj_name == "uuid"
280
+ path = [["hardware"], ["bios"]]
281
+ # Drives
282
+ elsif maj_name == "scsi" || maj_name == "ide" || maj_name == "floppy"
283
+ path = getDevicePath(maj, "disk")
284
+ # Ports
285
+ elsif maj_name == "usb" || maj_name == "parallel" || maj_name == "serial" || maj_name == "sound" || maj_name == "ethernet"
286
+ path = getDevicePath(maj, maj_name)
287
+ elsif maj_name == "snapshot"
288
+ other, min = min, other if other
289
+ path = getSnapShotPath(maj, maj_name, other)
290
+ else
291
+ path = [["vm"], [maj]]
292
+ end
293
+
294
+ parent = xml.root
295
+ path.each do |p|
296
+ if p[1].nil?
297
+ ele = MIQRexml.findElement(p[0], parent) if parent.has_elements?
298
+ else
299
+ ele = find_with_attributes(p[0], p[1], parent) if parent.has_elements?
300
+ end
301
+
302
+ # Create new node if needed.
303
+ if ele.nil?
304
+ # Remove any nil keys from the hash
305
+ p[1].delete_if { |_k, v| v.nil? } if p[1].kind_of?(Hash)
306
+ parent = parent.add_element(p[0], p[1])
307
+ else
308
+ parent, ele = ele, nil
309
+ end
310
+ end
311
+ parent.add_attribute(min.tr(':', '_'), value) if parent
312
+ end
313
+
314
+ def add_disk_stats(xml, miqvm)
315
+ # Now loop over the xml and find disk files
316
+ xml.find_first("/*/hardware").each_recursive do |e|
317
+ # Find elements that have a filename attribute
318
+ # Loop through the "whole disks" and get the size for this disk
319
+ if e.attributes['filename']
320
+ getDiskFileHash.each_pair do |device_id, filename|
321
+ if is_same_disk?(device_id, e)
322
+ if (fstat = find_file(e.attributes['filename'], disk_files(miqvm))).nil?
323
+ $log.warn "add_disk_stats - Disk file not found - Details: device_id: [#{device_id}] [#{filename}]"
324
+ next
325
+ end
326
+
327
+ $log.info "add_disk_stats - device_id: [#{device_id}] [#{filename}]"
328
+
329
+ whole_disk = nil
330
+ whole_disk = miqvm.wholeDisks.detect { |wd| wd.hwId.include?(device_id) } if @vol_mgr_loaded
331
+ size = whole_disk.nil? ? fstat[:provision_size] : whole_disk.size
332
+
333
+ # Report a disk's size_on_disk as the size of all the files that make up the disk.
334
+ # This includes the base disk and snapshots.
335
+ collective_disk_size = collective_size_on_disk(device_id, disk_files(miqvm))
336
+ size_on_disk = collective_disk_size.blank? ? fstat[:size] : collective_disk_size
337
+ e.add_attributes({'size' => size, 'size_on_disk' => size_on_disk, 'disk_type' => fstat[:disk_type]})
338
+ end
339
+ end
340
+ end
341
+ end
342
+ rescue Exception
343
+ $log.error "VmConfig.add_disk_stats [#{$!.class}]-[#{$!}]\n#{$!.backtrace.join("\n")}"
344
+ end
345
+
346
+ # Match filenames to loaded disks using the hardware id. IE scsi0:0 or ide0:0
347
+ def is_same_disk?(hardware_id, xml_element)
348
+ diskId = xml_element.parent.attributes["type"] + xml_element.attributes["id"].to_s
349
+ hardware_id.include?(diskId)
350
+ end
351
+
352
+ def normalize_path_names(xml)
353
+ # Now loop over the xml and find disk files
354
+ xml.root.each_recursive { |e| e.add_attribute('filename', resolve_ds_path(e.attributes['filename'])) if e.attributes['filename'] }
355
+ xml.find_first("/*/files").each_recursive { |e| e.add_attribute('name', resolve_ds_path(e.attributes['name'])) if e.attributes['name'] }
356
+ xml.find_first("/*/volumes").each_recursive { |e| e.add_attribute('virtual_disk_file', resolve_ds_path(e.attributes['virtual_disk_file'])) if e.attributes['virtual_disk_file'] }
357
+ end
358
+
359
+ def resolve_ds_path(filename)
360
+ # TODO: Determine if we need to do any work here.
361
+ ds, dir, name = split_filename(filename)
362
+ return filename unless ds.nil?
363
+
364
+ @ds_replace ||= {}
365
+ @ds_replace.each_pair do |path, ds|
366
+ return filename.sub(path, ds) if filename.include?(path)
367
+ end
368
+
369
+ ds_filename = resolve_ds_path_with_vim(filename)
370
+ ds, dir, name = split_filename(ds_filename)
371
+ unless ds.nil?
372
+ idx = filename.index(dir)
373
+ replaced_str = filename[0, idx]
374
+ @ds_replace[replaced_str] = "[#{ds}] "
375
+ end
376
+
377
+ ds_filename
378
+ end
379
+
380
+ def host_vim_credentials
381
+ creds = nil
382
+ if $miqHostCfg
383
+ if $miqHostCfg.emsLocal
384
+ creds = $miqHostCfg.ems[$miqHostCfg.emsLocal]
385
+ elsif $miqHostCfg.vimHost
386
+ c = $miqHostCfg.vimHost
387
+ return nil if c[:username].nil?
388
+ creds = {'host' => c[:address], 'user' => c[:username], 'password' => c[:password], 'use_vim_broker' => c[:use_vim_broker]}
389
+ end
390
+ end
391
+ creds
392
+ end
393
+
394
+ def connect_to_host_vim(conn_reason, vmCfgFile)
395
+ ems_host = host_vim_credentials
396
+ return nil if ems_host.nil?
397
+
398
+ begin
399
+ st = Time.now
400
+ ems_display_text = "host(#{ems_host['use_vim_broker'] ? 'via broker' : 'directly'}):#{ems_host['host']}"
401
+ $log.info "#{conn_reason}: Connecting to [#{ems_display_text}] for VM:[#{vmCfgFile}]"
402
+
403
+ require 'VMwareWebService/miq_fault_tolerant_vim'
404
+
405
+ password_decrypt = MiqPassword.decrypt(ems_host['password'])
406
+ hostVim = MiqFaultTolerantVim.new(:ip => ems_host['host'], :user => ems_host['user'], :pass => password_decrypt, :use_broker => ems_host['use_vim_broker'], :vim_broker_drb_port => ems_host['vim_broker_drb_port'])
407
+ $log.info "#{conn_reason}: Connection to [#{ems_display_text}] completed for VM:[#{vmCfgFile}] in [#{Time.now - st}] seconds"
408
+ return hostVim
409
+ rescue Timeout::Error => err
410
+ $log.error "#{conn_reason}: Connection to [#{ems_display_text}] timed out for VM:[#{vmCfgFile}] with error [#{err}] after [#{Time.now - st}] seconds"
411
+ rescue Exception => err
412
+ $log.error "#{conn_reason}: Connection to [#{ems_display_text}] failed for VM:[#{vmCfgFile}] with error [#{err}] after [#{Time.now - st}] seconds"
413
+ end
414
+ nil
415
+ end
416
+
417
+ def resolve_ds_path_with_vim(filename)
418
+ vi = nil
419
+ ems = host_vim_credentials
420
+ return (filename) if ems.nil?
421
+
422
+ password_decrypt = MiqPassword.decrypt(ems['password'])
423
+ $log.debug "resolve_path_names: emsHost = #{ems['host']}, emsUser = #{ems['user']}" if $log
424
+ vi = MiqVimInventory.new(ems['host'], ems['user'], password_decrypt)
425
+ return getDsName(filename, vi)
426
+
427
+ rescue
428
+ $log.error "VmConfig.resolve_ds_path_with_vim #{$!}\n#{$!.backtrace.join("\n")}"
429
+ ensure
430
+ vi.disconnect if vi
431
+ end
432
+
433
+ def getDsName(filename, vi)
434
+ filename = File.join(File.dirname(@configFile), filename) if File.dirname(filename) == "."
435
+ dsName = vi.datastorePath(filename)
436
+ dsName.sub!("] /", "] ") if dsName.index("] /")
437
+ return dsName
438
+ rescue
439
+ return filename
440
+ end
441
+
442
+ def files(miqvm = nil)
443
+ return @files if @files
444
+ log_header = "VmConfig.files"
445
+
446
+ @files = []
447
+ if @direct_file_access
448
+ Dir.glob(File.join(@configPath, "/*.*")) do |f|
449
+ s = File.sizeEx(f) rescue 0
450
+ @files << {:path => f, :name => File.basename(f), :size => s, :mtime => File.mtime(f)}
451
+ end
452
+ else
453
+ begin
454
+ if miqvm.vim
455
+ filePattern = nil
456
+ pathOnly = false
457
+ vimDs = nil
458
+ each_datastore(miqvm) do |ds, dirs|
459
+ begin
460
+ vimDs = miqvm.vim.getVimDataStore(ds)
461
+ dirs.each do |path|
462
+ vimDs.dsFileSearch(filePattern, path, pathOnly).each do |f|
463
+ @files << {:path => f['fullPath'], :name => f['path'], :size => f['fileSize'].to_i, :mtime => Time.parse(f['modification'])}
464
+ end
465
+ end
466
+ ensure
467
+ vimDs.release if vimDs rescue nil
468
+ end
469
+ end
470
+ elsif miqvm.rhevm
471
+ # First add the VM's active disk files.
472
+ miqvm.rhevmVm.disks.each { |disk| @files << rhevm_disk_file_entry(disk) }
473
+ # Then add the files associtaed with inactive snapshots.
474
+ miqvm.rhevmVm.snapshots.each do |snap|
475
+ snap.disks.each { |disk| @files << rhevm_disk_file_entry(disk) unless snap[:type] == 'active' }
476
+ end
477
+ end
478
+ rescue => err
479
+ $log.error "#{log_header} #{err}\n#{err.backtrace.join("\n")}"
480
+ end
481
+ end
482
+ disk_files(miqvm)
483
+ @files
484
+ end
485
+
486
+ def rhevm_disk_file_entry(disk)
487
+ storage_domain = disk[:storage_domains].first
488
+ storage_id = storage_domain && storage_domain[:id]
489
+ disk_key = disk[:image_id].blank? ? :id : :image_id
490
+ fullPath = storage_id && File.join('/dev', storage_id, disk[disk_key])
491
+
492
+ {:path => fullPath, :name => disk[disk_key], :size => disk[:actual_size].to_i}
493
+ end
494
+ private :rhevm_disk_file_entry
495
+
496
+ def disk_files(miqvm = nil)
497
+ return @disk_files if @disk_files
498
+ @disk_files = []
499
+ if @direct_file_access
500
+ @disk_files = files(miqvm)
501
+ else
502
+ begin
503
+ vim_disks = {}
504
+ if miqvm.vimVm
505
+ miqvm.vimVm.devices.each do |d|
506
+ next if d['capacityInKB'].nil?
507
+ next if (filename = d.fetch_path('backing', 'fileName')).nil?
508
+ disk = vim_disks[filename.to_s] = {}
509
+ disk[:disk_type] = (d.fetch_path('backing', 'thinProvisioned') == 'true') ? 'thin' : 'thick'
510
+ disk[:provision_size] = d['capacityInKB'].to_i * 1024
511
+ disk[:display_name] = d.fetch_path('deviceInfo', 'label').to_s
512
+ disk[:vim_index] = d['key'].to_i
513
+ disk[:disk_mode] = d['diskMode']
514
+ end
515
+
516
+ filePattern = nil
517
+ pathOnly = false
518
+ each_datastore(miqvm) do |ds, dirs|
519
+ begin
520
+ vimDs = miqvm.vim.getVimDataStore(ds)
521
+ dirs.each do |path|
522
+ vimDs.dsVmDiskFileSearch(filePattern, path, pathOnly).each do |f|
523
+ d = {:path => f['fullPath'], :name => f['path'].to_s, :size => f['fileSize'].to_i, :mtime => Time.parse(f['modification'])}
524
+ dh = vim_disks[d[:path]]
525
+ d.merge!(dh) unless dh.nil?
526
+ @disk_files << d
527
+ end
528
+ end
529
+ ensure
530
+ vimDs.release if vimDs rescue nil
531
+ end
532
+ end
533
+ elsif miqvm.rhevmVm
534
+ miqvm.rhevmVm.disks.each do |disk|
535
+ storage_domain = disk[:storage_domains].first
536
+ storage_id = storage_domain && storage_domain[:id]
537
+ disk_key = disk[:image_id].blank? ? :id : :image_id
538
+ fullPath = storage_id && File.join('/dev', storage_id, disk[disk_key])
539
+ d = {:path => fullPath, :name => disk[:name].to_s, :size => disk[:size].to_i}
540
+ @disk_files << d
541
+ end
542
+ end
543
+ rescue Exception
544
+ $log.error "VmConfig.disk_files #{$!}\n#{$!.backtrace.join("\n")}"
545
+ end
546
+ end
547
+ @disk_files
548
+ end
549
+
550
+ def add_file_sizes(xml, miqvm)
551
+ return if miqvm.nil?
552
+ begin
553
+ node = xml.root.add_element("files")
554
+ total_size = 0
555
+ files(miqvm).each do |fh|
556
+ total_size += fh[:size]
557
+ node.add_element('file', {"name" => fh[:name], "size_on_disk" => fh[:size]})
558
+ end
559
+ # Add the total size to the "files" node
560
+ node.add_attribute("size_on_disk", total_size)
561
+
562
+ free_space = 0; disk_capacity = 0
563
+ if miqvm && @vol_mgr_loaded
564
+ # Make sure we have the volume manager loaded
565
+ vmRoot = miqvm.rootTrees[0]
566
+ if vmRoot
567
+ miqvm.wholeDisks.each { |d| disk_capacity += d.size }
568
+ rootAdded = false
569
+ vmRoot.fileSystems.each do |fsd|
570
+ $log.info "FileSystem: #{fsd.fsSpec}, Mounted on: #{fsd.mountPoint}, Type: #{fsd.fs.fsType}, Free bytes: #{fsd.fs.freeBytes}"
571
+ # The root volume can appear more than once, so only add it one time.
572
+ if fsd.mountPoint == "/"
573
+ if rootAdded == false
574
+ free_space += fsd.fs.freeBytes
575
+ rootAdded = true
576
+ end
577
+ else
578
+ free_space += fsd.fs.freeBytes
579
+ end
580
+ end
581
+ end
582
+
583
+ # Now calculate free/used percent against size on disk
584
+ if !free_space.zero? && !disk_capacity.zero?
585
+ # Calculate the percentage of free space
586
+ percent_free = free_space.to_f / disk_capacity.to_f * 100
587
+
588
+ # Populate formated text fields
589
+ node.add_attributes("pct_free_wrt_size_on_disk" => percent_free, "pct_used_wrt_size_on_disk" => (100 - percent_free))
590
+ node.add_attributes("disk_free_space" => free_space, "disk_capacity" => disk_capacity)
591
+ end
592
+ end
593
+ node.add_attributes("disk_free_space" => free_space, "disk_capacity" => disk_capacity)
594
+ rescue
595
+ end
596
+ end
597
+
598
+ def load_vim_snapshots(miqvm)
599
+ if @direct_file_access == false && miqvm && miqvm.vim
600
+ # If the VM does not have snapshots we can stop here
601
+ return if miqvm.snapshots.nil?
602
+ ds, dir, name = split_filename(miqvm.vmConfigFile)
603
+
604
+ # We need to connect to the host to get the snapshot file content
605
+ hostVim = nil
606
+ if miqvm.vim.isVirtualCenter?
607
+ hostVim = connect_to_host_vim('snapshot_metadata', miqvm.vmConfigFile)
608
+ return if hostVim.nil?
609
+ vimDs = hostVim.getVimDataStore(ds)
610
+ else
611
+ vimDs = miqvm.vim.getVimDataStore(ds)
612
+ end
613
+
614
+ require "metadata/VmConfig/vmxConfig"
615
+ extend Kernel.const_get("VmxConfig")
616
+ snapshot_file = File.join(dir, File.basename(name, ".*") + ".vmsd")
617
+ Timeout.timeout(60) do
618
+ process_file(convert_vmsd(vimDs.get_file_content(snapshot_file)))
619
+ end
620
+ @cfgHash
621
+ end
622
+ rescue Timeout::Error => err
623
+ $log.warn "Timeout reached transferring snapshot metadata for config file [#{miqvm.vmConfigFile}]. Message:[#{err}]"
624
+ rescue => err
625
+ $log.warn "Unable to process snapshot metadata for config file [#{miqvm.vmConfigFile}]. Message:[#{err}]"
626
+ ensure
627
+ vimDs.release if vimDs rescue nil
628
+ hostVim.release if hostVim rescue nil
629
+ end
630
+
631
+ def each_datastore(_miqvm)
632
+ files = [@configFile]
633
+ each_disks { |_device_id, filename| files << filename }
634
+
635
+ ds = Hash.new { |h, k| h[k] = {} }
636
+ files.each do |f|
637
+ dsName, dir, _name = split_filename(f)
638
+ ds[dsName][dir] = true # unless dsName.nil?
639
+ end
640
+
641
+ ds.each_pair { |ds, h| yield(ds, h.to_a.transpose.first) }
642
+ end
643
+
644
+ def getDevicePath(name, endType)
645
+ # Initialize variables
646
+ full_pos, maj_pos, device = nil, "0", name
647
+ # If the name contains a number, use it as the id
648
+
649
+ if name.index(/\d/)
650
+ device = $`.chomp(':')
651
+ maj_pos = $&
652
+ full_pos = "#{$&}#{$'}"
653
+ end
654
+
655
+ # These devices only have 1 position number, and should be grouped under a controller
656
+ maj_pos = nil if ["ethernet", "floppy", "parallel", "serial"].include?(device)
657
+
658
+ ret = [["hardware"], ["bus", {"type" => "pci"}], ["controller", {"type" => device, "id" => maj_pos}]]
659
+ # Add a device type if the major position and full position differ, otherwise
660
+ # we are processing a controller element.
661
+ ret << ["device", {"type" => endType, "id" => full_pos}] if maj_pos != full_pos
662
+ ret
663
+ end
664
+
665
+ def getSnapShotPath(name, endType, disk)
666
+ # Initialize variables
667
+ full_pos, _maj_pos, _device, pos_idx = split_data(name)
668
+
669
+ return [["vm"], ["snapshots"]] if pos_idx.nil?
670
+ ret = [["vm"], ["snapshots"], [endType, {"id" => full_pos}]]
671
+
672
+ if disk
673
+ subDevice = split_data(disk)[2]
674
+ diskPath = getSnapShotPath(disk, subDevice, nil)
675
+ ret << diskPath.pop
676
+ end
677
+
678
+ ret
679
+ end
680
+
681
+ def add_snapshot_size(xml, _miqvm)
682
+ adjust_snapshot_aligment(xml)
683
+
684
+ # First we need to loop through the snapshots and add flat files for any
685
+ # disks that are just descriptor files
686
+ xml.find_each("//*/snapshots/snapshot/disk") do |e|
687
+ unless e.attributes['filename'].nil?
688
+ find_additional_disk_files(e.attributes['filename']) do |f|
689
+ e.parent.add_element("disk", "filename" => f[:path])
690
+ end
691
+ end
692
+ end
693
+
694
+ # Now loop through the filename and collect stats (date/time/size)
695
+ node = xml.find_first("//*/snapshots")
696
+ node.each_recursive do |e|
697
+ if e.attributes.include?("filename")
698
+ begin
699
+ filename = normalize_file_path(e.attributes['filename'])
700
+ next if (fstat = find_file(filename)).nil?
701
+ e.add_attribute("size_on_disk", fstat[:size])
702
+ e.add_attribute("mdate_on_disk", fstat[:mtime].getutc.iso8601(6))
703
+ rescue => err
704
+ # Ignore errors here, we will try to load almost anything.
705
+ $log.error "VmConfig.add_snapshot_size [#{err.class}]-[#{err}]\n#{err.backtrace.join("\n")}"
706
+ end
707
+ end
708
+ end unless node.nil?
709
+ end
710
+
711
+ def adjust_snapshot_aligment(xml)
712
+ sn = {}
713
+ sn_root = xml.find_first("//*/snapshots")
714
+
715
+ unless sn_root.nil?
716
+ disks_by_snap = disks_by_snapshot_and_node
717
+ xml.find_each("//*/snapshots/snapshot") { |s| sn[s.attributes[:uid]] = s }
718
+
719
+ sn_list = sn.sort { |a, b| a[1].attributes[:uid].to_s <=> b[1].attributes[:uid].to_s }
720
+ clear_snapshot_disks(sn_list)
721
+
722
+ sn_list.each do |(id, snapshot)|
723
+ parent = snapshot.attributes[:parent]
724
+ unless parent.nil? || sn[parent].nil?
725
+ disks_by_snap[id.to_s]['disks'].each do |device_id, filename|
726
+ sn[parent].add_element(:disk, :node => device_id, :filename => filename)
727
+ end
728
+ end
729
+
730
+ filename = normalize_file_path(snapshot.attributes[:filename])
731
+ snapshot.add_element("vmem", "filename" => filename) if find_file(filename)
732
+ end
733
+
734
+ current = sn[sn_root.attributes[:current]]
735
+ unless current.nil?
736
+ disks_by_snap[:current]['disks'].each do |device_id, filename|
737
+ current.add_element(:disk, :node => device_id, :filename => filename)
738
+ end
739
+ end
740
+ end
741
+ end
742
+
743
+ def clear_snapshot_disks(sn_list)
744
+ sn_list.each do |(_id, snapshot)|
745
+ delete_items = []
746
+ snapshot.each { |d| delete_items << d if d.name == 'disk' }
747
+ delete_items.each(&:remove!)
748
+ end
749
+ end
750
+
751
+ def split_data(name)
752
+ # Initialize variables
753
+ full_pos, maj_pos, device = nil, "0", name
754
+ # If the name contains a number, use it as the id
755
+ pos_idx = name.index(/\d/)
756
+ if pos_idx
757
+ full_pos = name[pos_idx..-1]
758
+ maj_pos, device = full_pos[0..0]
759
+ device = name[0...pos_idx]
760
+ end
761
+ return full_pos, maj_pos, device, pos_idx
762
+ end
763
+
764
+ def find_with_attributes(findKey, findAttr, xmlNode)
765
+ xmlNode.each_element do |e|
766
+ if e.name == findKey
767
+ found = true
768
+ findAttr.each_pair do |k, v|
769
+ found = false if e.attributes[k] != v
770
+ end
771
+ return e if found
772
+ end
773
+ end
774
+ nil
775
+ end
776
+
777
+ def add_volumes(xml, miqvm)
778
+ return unless @vol_mgr_loaded
779
+ return if miqvm.nil?
780
+ xml.root << miqvm.volumeManager.toXml.root
781
+ end
782
+
783
+ def configuration_fixup
784
+ fixup_keys = []
785
+ @cfgHash.each_pair do |k, v|
786
+ # If the cdrom does not have a filename force the 'startconnected' to false.
787
+ # This is to handle "Client Device" settings in VC that does not maintain the startconnected value.
788
+ if k.include?('.devicetype') && v.include?('cdrom')
789
+ dskKey = k.gsub('.devicetype', '')
790
+ fixup_keys << dskKey if @cfgHash[dskKey + ".filename"].to_s.delete('"').blank?
791
+ end
792
+ end
793
+ fixup_keys.each { |k| @cfgHash["#{k}.startconnected"] = 'false' }
794
+ end
795
+
796
+ def collective_size_on_disk(device_id, files = [])
797
+ total_size = 0
798
+ disks_by_node[device_id].each do |disk_name|
799
+ file = files.detect { |f| f[:path] == disk_name }
800
+ total_size += file[:size] unless file.nil?
801
+ find_additional_disk_files(disk_name) { |f| total_size += f[:size] } if @direct_file_access
802
+ end
803
+ total_size
804
+ end
805
+
806
+ def split_filename(filename)
807
+ file = filename.dup
808
+ ds = nil
809
+ if file =~ /^\[.*\] /
810
+ ds = $&.strip[1..-2]
811
+ file = $'
812
+ end
813
+ return ds, File.dirname(file), File.basename(file)
814
+ end
815
+
816
+ def base_disk_name(filename)
817
+ ds, dir, name = split_filename(filename)
818
+ if name =~ /-\d{6}[-|\.]/
819
+ # puts "#{$`}<<#{$&}>>#{$'}"
820
+ return nil if $`.nil?
821
+ basename = $`
822
+ else
823
+ basename = File.basename(name, '.*')
824
+ basename.gsub!('-flat', '')
825
+ end
826
+
827
+ key = File.join(dir, basename)
828
+ key = "[#{ds}] " + key unless ds.nil?
829
+ key
830
+ end
831
+
832
+ def self.to_h(filename)
833
+ dataHash = {}
834
+ data = filename
835
+ data = File.read(filename) unless filename.include?("\n")
836
+ data.each_line do |l|
837
+ l.strip!
838
+ next if l[0..0] == '.' || l[0..0] == '#' || l.empty?
839
+
840
+ parent = l.split('=')[0].split('.').inject(:current_level => dataHash, :hash => nil, :key => nil) do |h, k|
841
+ a1 = h[:current_level][k.strip.to_sym] ||= {}
842
+ a1 = h[:current_level][k.strip.to_sym] = {:_default => a1} unless a1.kind_of?(Hash)
843
+ {:current_level => a1, :hash => h, :key => k.strip.to_sym}
844
+ end
845
+ uh, key = parent[:hash][:current_level], parent[:key]
846
+ kkey = key.to_s.strip.to_sym
847
+ value = eval_config(l.split('=')[1], kkey)
848
+ uh[key] = value
849
+ end
850
+ dataHash
851
+ end
852
+
853
+ def self.eval_config(value, _key)
854
+ value.strip!
855
+ value = value[1..-2] if value[0, 1] == '"' && value[-1, 1] == '"'
856
+ [true, false].each { |b| return b if value.downcase == b.to_s }
857
+ return value.to_i if value =~ /^\d/
858
+ return nil if value.blank?
859
+ value
860
+ end
861
+
862
+ def normalize_file_paths(config_file = @configFile)
863
+ @cfgHash.each_pair do |k, v|
864
+ @cfgHash[k] = normalize_file_path(v, config_file) if k.include?('.filename')
865
+ end
866
+ end
867
+
868
+ def normalize_file_path(filename, config_file = @configFile)
869
+ cfg_ds, cfg_dir, _cfg_name = split_filename(config_file)
870
+ ds, dir, name = split_filename(filename)
871
+ if dir.blank? || dir == '.'
872
+ name = File.join(cfg_dir, name)
873
+ name = "[#{cfg_ds}] " + name unless cfg_ds.nil?
874
+ return name
875
+ else
876
+ # Check for files that do not have a ds when the config file does
877
+ return resolve_ds_path(filename) if cfg_ds && ds.nil?
878
+ end
879
+ filename
880
+ end
881
+
882
+ def disks_by_node
883
+ result = Hash.new { |h, k| h[k] = [] }
884
+
885
+ # Keep track of the disk we process so they do not get counted twice.
886
+ # This handles independent-persistent disk that do not take part in snapshots but will
887
+ # show up in each snapshot's disk list.
888
+ processed_disks = []
889
+
890
+ normalize_file_paths
891
+ getDiskFileHash.each_pair do |disk_id, path|
892
+ unless processed_disks.include?(path)
893
+ result[disk_id] << path
894
+ processed_disks << path
895
+ end
896
+ end
897
+
898
+ getSnapshotDiskFileHash.each_pair do |_id, h|
899
+ h['disks'].each_pair do |disk_id, path|
900
+ unless processed_disks.include?(path)
901
+ result[disk_id] << path
902
+ processed_disks << path
903
+ end
904
+ end
905
+ end
906
+
907
+ result
908
+ end
909
+
910
+ def each_disks
911
+ # Call disks_by_node since it will remove duplicate disks
912
+ disks_by_node.each_pair { |device_id, disks| disks.each { |d| yield(device_id, d) } }
913
+ end
914
+
915
+ def disks_by_snapshot_and_node
916
+ result = Hash.new { |h, k| h[k] = {} }
917
+ getSnapshotDiskFileHash.each_pair { |id, h| result[id] = h }
918
+
919
+ result[:current] = {'disks' => {}}
920
+ current_disks = result[:current]['disks']
921
+ getDiskFileHash.each_pair do |device_id, d|
922
+ # Independent disks do not par-take in snapshots. Check the mode of the parent disk.
923
+ next if @cfgHash["#{device_id}.mode"].to_s.include?('independent')
924
+ current_disks[device_id] = d
925
+ end
926
+
927
+ result
928
+ end
929
+
930
+ def find_additional_disk_files(filename)
931
+ ds, dir, name = split_filename(normalize_file_path(filename))
932
+ dfBase, ext = File.basename(name, ".*"), File.extname(name)
933
+ %w(-flat -delta).each do |disk_type|
934
+ search_filename = file_join(dir, dfBase + disk_type + ext)
935
+ search_filename = "[#{ds}] " + search_filename unless ds.nil?
936
+ f = find_file(search_filename)
937
+ yield(f) unless f.nil?
938
+ end
939
+ end
940
+
941
+ # Ensures that windows paths use forward slashes to full path lookups work.
942
+ def file_join(*args)
943
+ filename = File.join(*args)
944
+ filename.tr!('\\', '/') if filename[1, 1] == ':'
945
+ filename
946
+ end
947
+ end