├── .document ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── RELEASE_NOTES.md ├── Rakefile ├── VERSION ├── lib ├── rubyipmi.rb └── rubyipmi │ ├── commands │ ├── basecommand.rb │ └── mixins │ │ ├── power_mixin.rb │ │ └── sensors_mixin.rb │ ├── freeipmi │ ├── commands │ │ ├── basecommand.rb │ │ ├── bmc.rb │ │ ├── bmcconfig.rb │ │ ├── bmcdevice.rb │ │ ├── bmcinfo.rb │ │ ├── chassis.rb │ │ ├── chassisconfig.rb │ │ ├── fru.rb │ │ ├── lan.rb │ │ ├── power.rb │ │ └── sensors.rb │ ├── connection.rb │ └── errorcodes.rb │ ├── ipmitool │ ├── commands │ │ ├── basecommand.rb │ │ ├── bmc.rb │ │ ├── chassis.rb │ │ ├── chassisconfig.rb │ │ ├── fru.rb │ │ ├── lan.rb │ │ ├── power.rb │ │ └── sensors.rb │ ├── connection.rb │ └── errorcodes.rb │ ├── observablehash.rb │ └── version.rb ├── rubyipmi.gemspec └── spec ├── Vagrantfile ├── fixtures ├── freeipmi │ ├── bmc_config.txt │ ├── bmc_config_lan_conf.txt │ ├── bmc_info.txt │ ├── errors.txt │ ├── fru.txt │ └── sensors.txt └── ipmitool │ ├── bmc_info.txt │ ├── errors.txt │ ├── fru.txt │ ├── lan.txt │ └── sensors.txt ├── integration ├── bmc_spec.rb ├── chassis_config_spec.rb ├── chassis_spec.rb ├── connection_spec.rb ├── fru_spec.rb ├── lan_spec.rb ├── power_spec.rb ├── rubyipmi_spec.rb └── sensor_spec.rb ├── manifests └── default.pp ├── spec_helper.rb └── unit ├── freeipmi ├── bmc-info_spec.rb ├── bmc_spec.rb ├── connection_spec.rb ├── errorcodes_spec.rb ├── fru_spec.rb ├── lan_spec.rb └── sensors_spec.rb ├── ipmitool ├── bmc_spec.rb ├── connection_spec.rb ├── errorcodes_spec.rb ├── fru_spec.rb ├── lan_spec.rb └── sensors_spec.rb └── rubyipmi_spec.rb /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | ruby: 14 | - "2.5" 15 | - "2.6" 16 | - "2.7" 17 | - "3.0" 18 | - "3.1" 19 | - "3.2" 20 | - "3.3" 21 | - "3.4" 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Install Ruby ${{ matrix.ruby }} 25 | uses: ruby/setup-ruby@v1 26 | with: 27 | ruby-version: ${{ matrix.ruby }} 28 | bundler-cache: true 29 | - name: Run unit tests 30 | run: bundle exec rake unit 31 | - name: Verify gem builds 32 | run: gem build *.gemspec 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | coverage.data 4 | .idea/ 5 | # rdoc generated 6 | rdoc 7 | .vagrant/ 8 | # yard generated 9 | doc 10 | .yardoc 11 | Gemfile.lock 12 | # bundler 13 | .bundle 14 | Gemfile.lock 15 | # built gems 16 | *.gem 17 | 18 | # jeweler generated 19 | pkg 20 | .vagrant 21 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 22 | # 23 | # * Create a file at ~/.gitignore 24 | # * Include files you want ignored 25 | # * Run: git config --global core.excludesfile ~/.gitignore 26 | # 27 | # After doing this, these files will be ignored in all your git projects, 28 | # saving you from having to 'pollute' every project you touch with them 29 | # 30 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 31 | # 32 | # For MacOS: 33 | # 34 | #.DS_Store 35 | 36 | # For TextMate 37 | #*.tmproj 38 | #tmtags 39 | 40 | # For emacs: 41 | #*~ 42 | #\#* 43 | #.\#* 44 | 45 | # For vim: 46 | #*.swp 47 | 48 | # For redcar: 49 | #.redcar 50 | 51 | # For rubinius: 52 | #*.rbc 53 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Style/StringLiterals: 2 | Enabled: false 3 | AllCops: 4 | Exclude: 5 | - 'rubyipmi.gemspec' 6 | SignalException: 7 | EnforcedStyle: only_raise 8 | Documentation: 9 | Enabled: false 10 | ClassAndModuleChildren: 11 | Enabled: false 12 | HashSyntax: 13 | EnforcedStyle: hash_rockets 14 | ClassCheck: 15 | EnforcedStyle: kind_of? 16 | SpaceInsideHashLiteralBraces: 17 | EnforcedStyle: no_space 18 | Style/WordArray: 19 | Enabled: false 20 | 21 | 22 | # TODO: fix these 23 | Metrics/AbcSize: 24 | Max: 60 25 | Metrics/CyclomaticComplexity: 26 | Max: 19 27 | Metrics/LineLength: 28 | Max: 149 29 | Metrics/MethodLength: 30 | Max: 44 31 | Metrics/ModuleLength: 32 | Max: 128 33 | Metrics/PerceivedComplexity: 34 | Max: 22 35 | Style/SpecialGlobalVars: 36 | Exclude: 37 | - 'lib/rubyipmi.rb' 38 | - 'lib/rubyipmi/commands/basecommand.rb' 39 | - 'lib/rubyipmi/freeipmi/commands/fru.rb' 40 | - 'lib/rubyipmi/ipmitool/commands/fru.rb' 41 | Style/RescueModifier: 42 | Exclude: 43 | - 'lib/rubyipmi.rb' 44 | Style/RegexpLiteral: 45 | Exclude: 46 | - 'spec/spec_helper.rb' 47 | Style/PredicateName: 48 | Exclude: 49 | - 'lib/rubyipmi.rb' 50 | Lint/RescueException: 51 | Exclude: 52 | - 'spec/integration/rubyipmi_spec.rb' 53 | Style/AccessorMethodName: 54 | Exclude: 55 | - 'lib/rubyipmi/freeipmi/connection.rb' 56 | - 'lib/rubyipmi/ipmitool/connection.rb' 57 | 58 | # Fixed by deprecation 59 | Style/MethodName: 60 | Exclude: 61 | - 'lib/rubyipmi/commands/mixins/power_mixin.rb' 62 | 63 | # Don't understand 64 | Style/FileName: 65 | Exclude: 66 | - 'spec/unit/freeipmi/bmc-info_spec.rb' 67 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'coveralls', :require => false 7 | gem 'pry' 8 | gem 'pry-rescue' 9 | gem "rubocop", "~> 0.33.0", :require => false 10 | end 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | (This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.) 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations below. 27 | 28 | When we speak of free software, we are referring to freedom of use, 29 | not price. Our General Public Licenses are designed to make sure that 30 | you have the freedom to distribute copies of free software (and charge 31 | for this service if you wish); that you receive source code or can get 32 | it if you want it; that you can change the software and use pieces of 33 | it in new free programs; and that you are informed that you can do 34 | these things. 35 | 36 | To protect your rights, we need to make restrictions that forbid 37 | distributors to deny you these rights or to ask you to surrender these 38 | rights. These restrictions translate to certain responsibilities for 39 | you if you distribute copies of the library or if you modify it. 40 | 41 | For example, if you distribute copies of the library, whether gratis 42 | or for a fee, you must give the recipients all the rights that we gave 43 | you. You must make sure that they, too, receive or can get the source 44 | code. If you link other code with the library, you must provide 45 | complete object files to the recipients, so that they can relink them 46 | with the library after making changes to the library and recompiling 47 | it. And you must show them these terms so they know their rights. 48 | 49 | We protect your rights with a two-step method: (1) we copyright the 50 | library, and (2) we offer you this license, which gives you legal 51 | permission to copy, distribute and/or modify the library. 52 | 53 | To protect each distributor, we want to make it very clear that 54 | there is no warranty for the free library. Also, if the library is 55 | modified by someone else and passed on, the recipients should know 56 | that what they have is not the original version, so that the original 57 | author's reputation will not be affected by problems that might be 58 | introduced by others. 59 | 60 | Finally, software patents pose a constant threat to the existence of 61 | any free program. We wish to make sure that a company cannot 62 | effectively restrict the users of a free program by obtaining a 63 | restrictive license from a patent holder. Therefore, we insist that 64 | any patent license obtained for a version of the library must be 65 | consistent with the full freedom of use specified in this license. 66 | 67 | Most GNU software, including some libraries, is covered by the 68 | ordinary GNU General Public License. This license, the GNU Lesser 69 | General Public License, applies to certain designated libraries, and 70 | is quite different from the ordinary General Public License. We use 71 | this license for certain libraries in order to permit linking those 72 | libraries into non-free programs. 73 | 74 | When a program is linked with a library, whether statically or using 75 | a shared library, the combination of the two is legally speaking a 76 | combined work, a derivative of the original library. The ordinary 77 | General Public License therefore permits such linking only if the 78 | entire combination fits its criteria of freedom. The Lesser General 79 | Public License permits more lax criteria for linking other code with 80 | the library. 81 | 82 | We call this license the "Lesser" General Public License because it 83 | does Less to protect the user's freedom than the ordinary General 84 | Public License. It also provides other free software developers Less 85 | of an advantage over competing non-free programs. These disadvantages 86 | are the reason we use the ordinary General Public License for many 87 | libraries. However, the Lesser license provides advantages in certain 88 | special circumstances. 89 | 90 | For example, on rare occasions, there may be a special need to 91 | encourage the widest possible use of a certain library, so that it becomes 92 | a de-facto standard. To achieve this, non-free programs must be 93 | allowed to use the library. A more frequent case is that a free 94 | library does the same job as widely used non-free libraries. In this 95 | case, there is little to gain by limiting the free library to free 96 | software only, so we use the Lesser General Public License. 97 | 98 | In other cases, permission to use a particular library in non-free 99 | programs enables a greater number of people to use a large body of 100 | free software. For example, permission to use the GNU C Library in 101 | non-free programs enables many more people to use the whole GNU 102 | operating system, as well as its variant, the GNU/Linux operating 103 | system. 104 | 105 | Although the Lesser General Public License is Less protective of the 106 | users' freedom, it does ensure that the user of a program that is 107 | linked with the Library has the freedom and the wherewithal to run 108 | that program using a modified version of the Library. 109 | 110 | The precise terms and conditions for copying, distribution and 111 | modification follow. Pay close attention to the difference between a 112 | "work based on the library" and a "work that uses the library". The 113 | former contains code derived from the library, whereas the latter must 114 | be combined with the library in order to run. 115 | 116 | GNU LESSER GENERAL PUBLIC LICENSE 117 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 118 | 119 | 0. This License Agreement applies to any software library or other 120 | program which contains a notice placed by the copyright holder or 121 | other authorized party saying it may be distributed under the terms of 122 | this Lesser General Public License (also called "this License"). 123 | Each licensee is addressed as "you". 124 | 125 | A "library" means a collection of software functions and/or data 126 | prepared so as to be conveniently linked with application programs 127 | (which use some of those functions and data) to form executables. 128 | 129 | The "Library", below, refers to any such software library or work 130 | which has been distributed under these terms. A "work based on the 131 | Library" means either the Library or any derivative work under 132 | copyright law: that is to say, a work containing the Library or a 133 | portion of it, either verbatim or with modifications and/or translated 134 | straightforwardly into another language. (Hereinafter, translation is 135 | included without limitation in the term "modification".) 136 | 137 | "Source code" for a work means the preferred form of the work for 138 | making modifications to it. For a library, complete source code means 139 | all the source code for all modules it contains, plus any associated 140 | interface definition files, plus the scripts used to control compilation 141 | and installation of the library. 142 | 143 | Activities other than copying, distribution and modification are not 144 | covered by this License; they are outside its scope. The act of 145 | running a program using the Library is not restricted, and output from 146 | such a program is covered only if its contents constitute a work based 147 | on the Library (independent of the use of the Library in a tool for 148 | writing it). Whether that is true depends on what the Library does 149 | and what the program that uses the Library does. 150 | 151 | 1. You may copy and distribute verbatim copies of the Library's 152 | complete source code as you receive it, in any medium, provided that 153 | you conspicuously and appropriately publish on each copy an 154 | appropriate copyright notice and disclaimer of warranty; keep intact 155 | all the notices that refer to this License and to the absence of any 156 | warranty; and distribute a copy of this License along with the 157 | Library. 158 | 159 | You may charge a fee for the physical act of transferring a copy, 160 | and you may at your option offer warranty protection in exchange for a 161 | fee. 162 | 163 | 2. You may modify your copy or copies of the Library or any portion 164 | of it, thus forming a work based on the Library, and copy and 165 | distribute such modifications or work under the terms of Section 1 166 | above, provided that you also meet all of these conditions: 167 | 168 | a) The modified work must itself be a software library. 169 | 170 | b) You must cause the files modified to carry prominent notices 171 | stating that you changed the files and the date of any change. 172 | 173 | c) You must cause the whole of the work to be licensed at no 174 | charge to all third parties under the terms of this License. 175 | 176 | d) If a facility in the modified Library refers to a function or a 177 | table of data to be supplied by an application program that uses 178 | the facility, other than as an argument passed when the facility 179 | is invoked, then you must make a good faith effort to ensure that, 180 | in the event an application does not supply such function or 181 | table, the facility still operates, and performs whatever part of 182 | its purpose remains meaningful. 183 | 184 | (For example, a function in a library to compute square roots has 185 | a purpose that is entirely well-defined independent of the 186 | application. Therefore, Subsection 2d requires that any 187 | application-supplied function or table used by this function must 188 | be optional: if the application does not supply it, the square 189 | root function must still compute square roots.) 190 | 191 | These requirements apply to the modified work as a whole. If 192 | identifiable sections of that work are not derived from the Library, 193 | and can be reasonably considered independent and separate works in 194 | themselves, then this License, and its terms, do not apply to those 195 | sections when you distribute them as separate works. But when you 196 | distribute the same sections as part of a whole which is a work based 197 | on the Library, the distribution of the whole must be on the terms of 198 | this License, whose permissions for other licensees extend to the 199 | entire whole, and thus to each and every part regardless of who wrote 200 | it. 201 | 202 | Thus, it is not the intent of this section to claim rights or contest 203 | your rights to work written entirely by you; rather, the intent is to 204 | exercise the right to control the distribution of derivative or 205 | collective works based on the Library. 206 | 207 | In addition, mere aggregation of another work not based on the Library 208 | with the Library (or with a work based on the Library) on a volume of 209 | a storage or distribution medium does not bring the other work under 210 | the scope of this License. 211 | 212 | 3. You may opt to apply the terms of the ordinary GNU General Public 213 | License instead of this License to a given copy of the Library. To do 214 | this, you must alter all the notices that refer to this License, so 215 | that they refer to the ordinary GNU General Public License, version 2, 216 | instead of to this License. (If a newer version than version 2 of the 217 | ordinary GNU General Public License has appeared, then you can specify 218 | that version instead if you wish.) Do not make any other change in 219 | these notices. 220 | 221 | Once this change is made in a given copy, it is irreversible for 222 | that copy, so the ordinary GNU General Public License applies to all 223 | subsequent copies and derivative works made from that copy. 224 | 225 | This option is useful when you wish to copy part of the code of 226 | the Library into a program that is not a library. 227 | 228 | 4. You may copy and distribute the Library (or a portion or 229 | derivative of it, under Section 2) in object code or executable form 230 | under the terms of Sections 1 and 2 above provided that you accompany 231 | it with the complete corresponding machine-readable source code, which 232 | must be distributed under the terms of Sections 1 and 2 above on a 233 | medium customarily used for software interchange. 234 | 235 | If distribution of object code is made by offering access to copy 236 | from a designated place, then offering equivalent access to copy the 237 | source code from the same place satisfies the requirement to 238 | distribute the source code, even though third parties are not 239 | compelled to copy the source along with the object code. 240 | 241 | 5. A program that contains no derivative of any portion of the 242 | Library, but is designed to work with the Library by being compiled or 243 | linked with it, is called a "work that uses the Library". Such a 244 | work, in isolation, is not a derivative work of the Library, and 245 | therefore falls outside the scope of this License. 246 | 247 | However, linking a "work that uses the Library" with the Library 248 | creates an executable that is a derivative of the Library (because it 249 | contains portions of the Library), rather than a "work that uses the 250 | library". The executable is therefore covered by this License. 251 | Section 6 states terms for distribution of such executables. 252 | 253 | When a "work that uses the Library" uses material from a header file 254 | that is part of the Library, the object code for the work may be a 255 | derivative work of the Library even though the source code is not. 256 | Whether this is true is especially significant if the work can be 257 | linked without the Library, or if the work is itself a library. The 258 | threshold for this to be true is not precisely defined by law. 259 | 260 | If such an object file uses only numerical parameters, data 261 | structure layouts and accessors, and small macros and small inline 262 | functions (ten lines or less in length), then the use of the object 263 | file is unrestricted, regardless of whether it is legally a derivative 264 | work. (Executables containing this object code plus portions of the 265 | Library will still fall under Section 6.) 266 | 267 | Otherwise, if the work is a derivative of the Library, you may 268 | distribute the object code for the work under the terms of Section 6. 269 | Any executables containing that work also fall under Section 6, 270 | whether or not they are linked directly with the Library itself. 271 | 272 | 6. As an exception to the Sections above, you may also combine or 273 | link a "work that uses the Library" with the Library to produce a 274 | work containing portions of the Library, and distribute that work 275 | under terms of your choice, provided that the terms permit 276 | modification of the work for the customer's own use and reverse 277 | engineering for debugging such modifications. 278 | 279 | You must give prominent notice with each copy of the work that the 280 | Library is used in it and that the Library and its use are covered by 281 | this License. You must supply a copy of this License. If the work 282 | during execution displays copyright notices, you must include the 283 | copyright notice for the Library among them, as well as a reference 284 | directing the user to the copy of this License. Also, you must do one 285 | of these things: 286 | 287 | a) Accompany the work with the complete corresponding 288 | machine-readable source code for the Library including whatever 289 | changes were used in the work (which must be distributed under 290 | Sections 1 and 2 above); and, if the work is an executable linked 291 | with the Library, with the complete machine-readable "work that 292 | uses the Library", as object code and/or source code, so that the 293 | user can modify the Library and then relink to produce a modified 294 | executable containing the modified Library. (It is understood 295 | that the user who changes the contents of definitions files in the 296 | Library will not necessarily be able to recompile the application 297 | to use the modified definitions.) 298 | 299 | b) Use a suitable shared library mechanism for linking with the 300 | Library. A suitable mechanism is one that (1) uses at run time a 301 | copy of the library already present on the user's computer system, 302 | rather than copying library functions into the executable, and (2) 303 | will operate properly with a modified version of the library, if 304 | the user installs one, as long as the modified version is 305 | interface-compatible with the version that the work was made with. 306 | 307 | c) Accompany the work with a written offer, valid for at 308 | least three years, to give the same user the materials 309 | specified in Subsection 6a, above, for a charge no more 310 | than the cost of performing this distribution. 311 | 312 | d) If distribution of the work is made by offering access to copy 313 | from a designated place, offer equivalent access to copy the above 314 | specified materials from the same place. 315 | 316 | e) Verify that the user has already received a copy of these 317 | materials or that you have already sent this user a copy. 318 | 319 | For an executable, the required form of the "work that uses the 320 | Library" must include any data and utility programs needed for 321 | reproducing the executable from it. However, as a special exception, 322 | the materials to be distributed need not include anything that is 323 | normally distributed (in either source or binary form) with the major 324 | components (compiler, kernel, and so on) of the operating system on 325 | which the executable runs, unless that component itself accompanies 326 | the executable. 327 | 328 | It may happen that this requirement contradicts the license 329 | restrictions of other proprietary libraries that do not normally 330 | accompany the operating system. Such a contradiction means you cannot 331 | use both them and the Library together in an executable that you 332 | distribute. 333 | 334 | 7. You may place library facilities that are a work based on the 335 | Library side-by-side in a single library together with other library 336 | facilities not covered by this License, and distribute such a combined 337 | library, provided that the separate distribution of the work based on 338 | the Library and of the other library facilities is otherwise 339 | permitted, and provided that you do these two things: 340 | 341 | a) Accompany the combined library with a copy of the same work 342 | based on the Library, uncombined with any other library 343 | facilities. This must be distributed under the terms of the 344 | Sections above. 345 | 346 | b) Give prominent notice with the combined library of the fact 347 | that part of it is a work based on the Library, and explaining 348 | where to find the accompanying uncombined form of the same work. 349 | 350 | 8. You may not copy, modify, sublicense, link with, or distribute 351 | the Library except as expressly provided under this License. Any 352 | attempt otherwise to copy, modify, sublicense, link with, or 353 | distribute the Library is void, and will automatically terminate your 354 | rights under this License. However, parties who have received copies, 355 | or rights, from you under this License will not have their licenses 356 | terminated so long as such parties remain in full compliance. 357 | 358 | 9. You are not required to accept this License, since you have not 359 | signed it. However, nothing else grants you permission to modify or 360 | distribute the Library or its derivative works. These actions are 361 | prohibited by law if you do not accept this License. Therefore, by 362 | modifying or distributing the Library (or any work based on the 363 | Library), you indicate your acceptance of this License to do so, and 364 | all its terms and conditions for copying, distributing or modifying 365 | the Library or works based on it. 366 | 367 | 10. Each time you redistribute the Library (or any work based on the 368 | Library), the recipient automatically receives a license from the 369 | original licensor to copy, distribute, link with or modify the Library 370 | subject to these terms and conditions. You may not impose any further 371 | restrictions on the recipients' exercise of the rights granted herein. 372 | You are not responsible for enforcing compliance by third parties with 373 | this License. 374 | 375 | 11. If, as a consequence of a court judgment or allegation of patent 376 | infringement or for any other reason (not limited to patent issues), 377 | conditions are imposed on you (whether by court order, agreement or 378 | otherwise) that contradict the conditions of this License, they do not 379 | excuse you from the conditions of this License. If you cannot 380 | distribute so as to satisfy simultaneously your obligations under this 381 | License and any other pertinent obligations, then as a consequence you 382 | may not distribute the Library at all. For example, if a patent 383 | license would not permit royalty-free redistribution of the Library by 384 | all those who receive copies directly or indirectly through you, then 385 | the only way you could satisfy both it and this License would be to 386 | refrain entirely from distribution of the Library. 387 | 388 | If any portion of this section is held invalid or unenforceable under any 389 | particular circumstance, the balance of the section is intended to apply, 390 | and the section as a whole is intended to apply in other circumstances. 391 | 392 | It is not the purpose of this section to induce you to infringe any 393 | patents or other property right claims or to contest validity of any 394 | such claims; this section has the sole purpose of protecting the 395 | integrity of the free software distribution system which is 396 | implemented by public license practices. Many people have made 397 | generous contributions to the wide range of software distributed 398 | through that system in reliance on consistent application of that 399 | system; it is up to the author/donor to decide if he or she is willing 400 | to distribute software through any other system and a licensee cannot 401 | impose that choice. 402 | 403 | This section is intended to make thoroughly clear what is believed to 404 | be a consequence of the rest of this License. 405 | 406 | 12. If the distribution and/or use of the Library is restricted in 407 | certain countries either by patents or by copyrighted interfaces, the 408 | original copyright holder who places the Library under this License may add 409 | an explicit geographical distribution limitation excluding those countries, 410 | so that distribution is permitted only in or among countries not thus 411 | excluded. In such case, this License incorporates the limitation as if 412 | written in the body of this License. 413 | 414 | 13. The Free Software Foundation may publish revised and/or new 415 | versions of the Lesser General Public License from time to time. 416 | Such new versions will be similar in spirit to the present version, 417 | but may differ in detail to address new problems or concerns. 418 | 419 | Each version is given a distinguishing version number. If the Library 420 | specifies a version number of this License which applies to it and 421 | "any later version", you have the option of following the terms and 422 | conditions either of that version or of any later version published by 423 | the Free Software Foundation. If the Library does not specify a 424 | license version number, you may choose any version ever published by 425 | the Free Software Foundation. 426 | 427 | 14. If you wish to incorporate parts of the Library into other free 428 | programs whose distribution conditions are incompatible with these, 429 | write to the author to ask for permission. For software which is 430 | copyrighted by the Free Software Foundation, write to the Free 431 | Software Foundation; we sometimes make exceptions for this. Our 432 | decision will be guided by the two goals of preserving the free status 433 | of all derivatives of our free software and of promoting the sharing 434 | and reuse of software generally. 435 | 436 | NO WARRANTY 437 | 438 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 439 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 440 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 441 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 442 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 443 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 444 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 445 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 446 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 447 | 448 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 449 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 450 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 451 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 452 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 453 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 454 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 455 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 456 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 457 | DAMAGES. 458 | 459 | END OF TERMS AND CONDITIONS 460 | 461 | Any questions please contact Corey Osman at corey@logicminds.biz 462 | 463 | Copyright (c) 2014 Corey Osman 464 | 465 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Table of Contents 2 | ================= 3 | 4 | * [Rubyipmi](#rubyipmi) 5 | * [Projects that use Rubyipmi](#projects-that-use-rubyipmi) 6 | * [Support](#support) 7 | * [Using the library in your code](#using-the-library-in-your-code) 8 | * [Requirements](#requirements) 9 | * [Create a connection object](#create-a-connection-object) 10 | * [Use power functions (not all listed)](#use-power-functions-not-all-listed) 11 | * [Boot to specific device](#boot-to-specific-device) 12 | * [Sensors](#sensors) 13 | * [Fru](#fru) 14 | * [Testing](#testing) 15 | * [Security](#security) 16 | * [How the library works](#how-the-library-works) 17 | * [Creating a new command](#creating-a-new-command) 18 | * [Writing a function for running a command](#writing-a-function-for-running-a-command) 19 | * [Running the cmd](#running-the-cmd) 20 | * [The Options hash](#the-options-hash) 21 | * [How to get the results of the command](#how-to-get-the-results-of-the-command) 22 | * [The command function](#the-command-function) 23 | * [The following are tools bundled with freeipmi](#the-following-are-tools-bundled-with-freeipmi) 24 | * [To contrast ipmitool has one command with many options](#to-contrast-ipmitool-has-one-command-with-many-options) 25 | * [Auto Detect workarounds](#auto-detect-workarounds) 26 | * [Troubleshooting](#troubleshooting) 27 | * [Log files](#log-files) 28 | * [Diagnostics Function](#diagnostics-function) 29 | * [Test Function](#test-function) 30 | * [Contributing to rubyipmi](#contributing-to-rubyipmi) 31 | * [Copyright](#copyright) 32 | * [Freeipmi Documented Workarounds](#freeipmi-documented-workarounds) 33 | 34 | # Rubyipmi 35 | This gem is a ruby wrapper for the freeipmi and ipmitool command line tools. 36 | It provides a ruby implementation of ipmi commands that will make it simple to connect to BMC devices from ruby. 37 | 38 | [![Build Status](https://travis-ci.org/logicminds/rubyipmi.png)](https://travis-ci.org/logicminds/rubyipmi) 39 | [![Gem Version](https://badge.fury.io/rb/rubyipmi.png)](http://badge.fury.io/rb/rubyipmi) 40 | [![Coverage Status](https://coveralls.io/repos/logicminds/rubyipmi/badge.png)](https://coveralls.io/r/logicminds/rubyipmi) 41 | 42 | Rubyipmi was built because I wanted an object oriented way to get data from BMC devices. I also wanted it easy to use 43 | and if any IPMI hacks/workarounds were required I wanted to build those into the library to make life easier. 44 | 45 | ## Projects that use Rubyipmi 46 | * https://github.com/sensu/sensu-community-plugins/blob/master/plugins/ipmi/check-sensor.rb 47 | * https://github.com/theforeman/smart-proxy (Turns Rubyipmi into a Remote Web API Proxy server) 48 | * https://github.com/logicminds/ipmispec (just started) 49 | 50 | Don't see your project listed? Create a PR with your project listed here. 51 | 52 | ## Support 53 | General support is offered via github issues and whenever I have time I will try to resolve any issues. I do offer 54 | professional paid support through my [company](http://www.logicminds.biz) and can be contracted to work directly with your organization 55 | on Rubyipmi or other related automation/devops projects that you might have. 56 | 57 | At this time I only have one test server (HP DL380 G5) which I used to perform tests against. While I try to keep the code 58 | generic in nature there may be newer devices that do not work correctly and its an issue I cannot troubleshoot directly. If you would 59 | like to see newer/other devices be part of my test suite and you have extra servers lying around. I would encourage you to donate 60 | server equipment so that I can extend my test suite against this newer equipment. 61 | 62 | Servers I have never tested against. 63 | 64 | * Dell 65 | * IBM 66 | * HP server with ilo3+ 67 | * Super Micro 68 | * Cisco 69 | 70 | IPMI is designed to support any equipment that implements the standard. But there are always problems with people deviating 71 | from the standard. In general this library should work will all servers. 72 | 73 | ## Using the library in your code 74 | 75 | ### Requirements 76 | 77 | 78 | 1. Install the freeipmi from source (http://www.gnu.org/software/freeipmi/) or ipmitool 79 | 2. `gem install rubyipmi` 80 | 81 | ### Create a connection object 82 | 83 | ```ruby 84 | require 'rubyipmi' 85 | conn = Rubyipmi.connect("username", "password", "hostname", "providertype") 86 | ``` 87 | 88 | Additionally, if your using [openipmi](http://openipmi.sourceforge.net) and will be using rubyipmi to connect to 89 | the 
localhost you can utilize the openipmi driver and not have to pass in any connection parameters. 90 | Openipmi works
by installing a driver and makes it available to the host. Freeipmi/Ipmitool will then try to use 91 | this driver to
automatically use the openipmi if no host is given. The one caveat here is that you cannot control 92 | remote hosts using
openipmi. The rubyipmi code must be executed on the host you want to control. 93 | The upside is that you don't need
any credentials. Some commands may require root privileges to run when using openipmi. 94 | 95 | Providertype: optional 96 | 97 | valid options: 'auto', 'ipmitool', 'freeipmi' 98 | 99 | If you don't specify the provider type, Rubyipmi will detect if freeipmi or ipmitool 100 | is installed and load the first tool found. If you specify the provider type rubyipmi will only use that specific 101 | provider. 102 | 103 | You can specify additional options by passing an options hash into the connection method 104 | ```ruby 105 | conn = Rubyipmi.connect("username", "password", "hostname", 'freeipmi', {:privilege =>'USER', :driver => 'lan20'}) 106 | ``` 107 | 108 | Privilege 109 | 110 | This option controls the role of the user making the ipmi call. 111 | valid options: 'CALLBACK', 'USER', 'OPERATOR', 'ADMINISTRATOR' -- defaults to nil and uses the freeipmi/ipmitool default 112 | 113 | Driver 114 | 115 | This option allows you to control which driver to use when making IPMI calls. Selecting auto will choose 116 | either lan15 or lan20 based on your device. The open type is when using openipmi in conjunction with the provider. 117 | valid options: "auto", "lan15", "lan20", "open" -- defaults to lan20 118 | 119 | 120 | ### power functions 121 | 122 | ```ruby 123 | require 'rubyipmi' 124 | conn = Rubyipmi.connect("username", "password", "hostname") 125 | conn.chassis.power.on 126 | conn.chassis.power.off 127 | conn.chassis.power.on? 128 | conn.chassis.power.off? 129 | conn.chassis.power.cycle 130 | 131 | ``` 132 | 133 | ### Boot to specific device 134 | 135 | ```ruby 136 | require 'rubyipmi' 137 | conn = Rubyipmi.connect("username", "password", "hostname") 138 | conn.chassis.bootpxe(reboot=bool, persistent=bool) 139 | conn.chassis.bootdisk(reboot=bool, persistent=bool) 140 | 141 | Examples: 142 | conn.chassis.bootpxe(reboot=true, persistent=false) # reboot immediately, PXE boot once 143 | conn.chassis.bootpxe(reboot=false, persistent=false) # on next reboot PXE boot once 144 | conn.chassis.bootdisk(reboot=true, persistent=false) # reboot immediately, boot off disk once 145 | conn.chassis.bootdisk(reboot=true, persistent=true) # reboot immediately, boot off disk forever 146 | conn.chassis.bootdisk(reboot=false, persistent=true) # reboot off disk forever, starting on next reboot 147 | ``` 148 | 149 | 150 | ### Sensors 151 | 152 | ```ruby 153 | require 'rubyipmi' 154 | conn = Rubyipmi.connect("username", "password", "hostname") 155 | conn.sensors.names 156 | conn.sensors.list 157 | conn.sensors. 158 | 159 | ``` 160 | 161 | ### Fru 162 | 163 | ```ruby 164 | require 'rubyipmi' 165 | conn = Rubyipmi.connect("username", "password", "hostname") 166 | conn.fru.list 167 | conn.fru.serial 168 | conn.fru.manufacturer 169 | conn.fru.product 170 | 171 | ``` 172 | 173 | ## Testing 174 | There are a series of automated rspec tests that test the functionality of this library with the ipmi device. 175 | In order to perform use the following steps. 176 | 177 | DO NOT PERFORM THESE TEST ON ANY PRODUCTION SYSTEM. THESE TESTS WILL TURN OFF THE DEVICE! 178 | 179 | 180 | 1. Install gem via source 181 | 2. bundle install 182 | 3. rake (runs unit tests, does not require a ipmi device) 183 | 3. rake integration ipmiuser=ipmiuser ipmipass=ipmiuserpass ipmihost=192.168.1.22 ipmiprovider=freeipmi (fill in your your details) 184 | 4. report any failures with your make/model/firmware revision to corey@logicminds.biz 185 | 186 | ## Security 187 | The only security used throughout the library is the use of temporary password files that store the password while 188 | the command is being executed. This password file is created and deleted on the fly with every library call. 189 | The password will not be shown in any logs or process lists due to this enhancement. The filename is a long random string 190 | as is the folder name so it would be difficult to guess the password file. If for some reason a command takes a long 191 | time to run anyone could get the location of the file but the file is 0600 so it should not be readable by anyone outside 192 | of the process. 193 | 194 | 195 | ## How the library works 196 | Since this library is based off of running a suite of command line tools I have created a base class called baseCommand 197 | that performs the actual execution of the command. The baseCommand only executes the command with the supplied 198 | arguments and options and returns the exit status. Additionally the result of the executed command is stored in 199 | the result variable should we need to retrieve the output of the command. To further extend the baseCommand class. 200 | 201 | 202 | ### Creating a new command 203 | Creating a new command is actually quite simple. Follow these steps to wrap a freeipmi or ipmitool command. 204 | 205 | 1. Create a new subclass of BaseCommand 206 | 2. define the initialize function like so, and pass in the name of the command line tool to the super constructor. 207 | 208 | ```ruby 209 | def initialize(opts = {}) 210 | @options = opts 211 | super("bmc-info", opts) 212 | end 213 | 214 | ``` 215 | 216 | ```ruby 217 | def initialize(opts = {}) 218 | @options = opts 219 | super("ipmitool", opts) 220 | end 221 | 222 | ``` 223 | 3. Thats it. The rest of the class is related to running the command and interperting the results 224 | 225 | ### Writing a function for running a command 226 | The freeipmi command line tools have two different ways to run the commands. 227 | 228 | 1. ``` ipmipower --hostname=host --password=pass --username=user --off ``` (single action, multiple arguments) 229 | 2. ``` ipmi-chassis --hostname=host --password=pass --username=user --chassis-identify=FORCE ``` (multiple arguments, one main action with different qualifers) 230 | 231 | Because of the varying ways to run a command I have simplified them so it makes it easy to call the command line tools. 232 | 233 | 1. Each subclassed baseCommand class inherits runcmd, result, cmd, and runcmd_with_args. 234 | 2. The cmd executable gets set when instantiating a baseCommand class, where the class also finds the path of the executable. 235 | 3. The options variable gets set when instantiating a subclass of the baseCommand class. 236 | 237 | ### Running the cmd 238 | 239 | 1. To run the cmd, just call ``` runcmd ``` (which will automatically set all the options specified in the options hash) 240 | 2. To run the cmd, with arguments and options call ``` runcmd([]) ``` and pass in a array of the arguments. (Arguments are actions only and look like --off, --on, --action) 241 | 3. To run the cmd, with just arguments ``` runcmd_with_args([]) ``` and pass in a array of the arguments. (Example: 192.168.1.1) 242 | 243 | ### The Options hash 244 | The options hash can be considered a global hash that is passed in through the connection object. 245 | Most of the options will be set at the connection level. However, most commands require additional options 246 | that should be set at the subclassed BaseCommand level. You must not forget to unset the option after calling 247 | the runcmd command. Failure to do so will add previous options to subsequent run options. 248 | 249 | Example: 250 | 251 | ```ruby 252 | def ledlight(status=false, delay=300) 253 | if status 254 | if delay <= 0 255 | options["chassis-identify"] = "FORCE" 256 | else 257 | options["chassis-identify"] = delay 258 | end 259 | else 260 | options["chassis-identify"] = "TURN-OFF" 261 | end 262 | # Run the command 263 | run 264 | # unset the option by deleting from hash 265 | options.delete("chassis-identify") 266 | end 267 | 268 | ``` 269 | 270 | ### How to get the results of the command 271 | After running a command it may be desirable to get the results for further processing. 272 | Note that there are two kinds of results. 273 | 1. the text returned from the shell command, this is stored in @results 274 | 2. the status value returned from the shell command (true or false only) this is returned from runcmd. 275 | 276 | To get the results: 277 | 278 | Example: 279 | 280 | ```ruby 281 | 282 | def status 283 | value = command("--stat") 284 | if value == true 285 | @result.split(":").last.chomp.trim 286 | end 287 | end 288 | 289 | def command(opt) 290 | status = run([opt]) 291 | return status 292 | end 293 | 294 | def on? 295 | status == "on" 296 | end 297 | 298 | ``` 299 | 300 | ### The command function 301 | Although its not necessary to implement the command function it may be desirable if your code starts to repeat itself. 302 | In this example the command function is just a wrapper command that calls run. Your implementation will vary, 303 | but be sure to always call it the "command" function, so its easily identified. 304 | Additionally, should this gem ever become out of date one could call the command function and pass in any 305 | arguments that have not already been implemented in the rest of the class. 306 | 307 | ```ruby 308 | 309 | def command(opt) 310 | status = runcmd([opt]) 311 | return status 312 | end 313 | 314 | def on 315 | command("--on") 316 | end 317 | 318 | ``` 319 | 320 | ## The following are tools bundled with freeipmi 321 | 322 | * ipmi-chassis 323 | * ipmi-oem 324 | * ipmi-chassis-config 325 | * ipmiping 326 | * ipmiconsole 327 | * ipmipower 328 | * ipmidetect 329 | * ipmi-raw 330 | * ipmi-fru 331 | * ipmi-sel 332 | * ipmi-locate 333 | * ipmi-sensors 334 | * ipmimonitoring 335 | * ipmi-sensors-config 336 | * bmc-config 337 | * bmc-device 338 | * bmc-info 339 | 340 | ## To contrast ipmitool has one command with many options 341 | * ipmitool 342 | 343 | 344 | 345 | ## Auto Detect workarounds 346 | IPMI is great for a vendor neutral management interface. However, not all servers are 100% compatible with the specifications. 347 | In order to overcome ipmi non-compliance there will be some workarounds built into this library 348 | 349 | ## Troubleshooting 350 | 351 | ### Log files 352 | Rubyipmi has a built in logging system for debugging purposes. By default logging is disabled. The logger is a class instance 353 | variable and will stay in memory for as long as your program or interpreter is loaded. In order to enable logging 354 | you need to do the following. 355 | 356 | ```ruby 357 | require 'rubyipmi' 358 | require 'logger' 359 | Rubyipmi.log_level = Logger::DEBUG 360 | ``` 361 | This will create a log file in /tmp/rubyipmi.log which you can use to trace the commands Rubyipmi generates and runs. 362 | 363 | If you want to setup a custom logger (not required) you can also pass in a logger instance as well. 364 | 365 | ```ruby 366 | require 'rubyipmi' 367 | require 'logger' 368 | custom_logger = Logger.new('/var/log/rubyipmi_custom.log') 369 | custom_logger.progname = 'Rubyipmi' 370 | custom_logger.level = Logger::DEBUG 371 | Rubyipmi.logger = custom_logger 372 | ``` 373 | 374 | ### Diagnostics Function 375 | Running IPMI commands can be frustrating sometimes and with the addition of this library you are bound to find edge 376 | cases. If you do find an edge case there is a easy function that will generate a diagnostics file that you can 377 | review and optionally create an issue with us to work with. Without this information its really hard to help because 378 | every server is different. The following code will generate a file in /tmp/rubyipmi_diag_data.txt that we can use 379 | as test cases. Please look over the file for any sensitive data you don't want to share like ip/mac address. 380 | 381 | ```ruby 382 | require 'rubyipmi' 383 | Rubyipmi.get_diag(user, pass, host) 384 | ``` 385 | 386 | You can couple this with the logger and also generate a log file of all the commands get_diag uses as well. 387 | 388 | ```ruby 389 | require 'rubyipmi' 390 | require 'logger' 391 | Rubyipmi.log_level = Logger::DEBUG 392 | Rubyipmi.get_diag(user, pass, host) 393 | ``` 394 | 395 | ### Test Function 396 | If you need to test if the bmc device and run a basic call there is now a function that retruns boolean true when 397 | the connection attempt was successful. 398 | 399 | ```ruby 400 | require 'rubyipmi' 401 | conn = Rubyipmi.connect(user, pass, host) 402 | conn.connection_works? => true|false 403 | ``` 404 | 405 | ## Contributing to rubyipmi 406 | 407 | * Check out the latest code to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. 408 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. 409 | * Fork the project. 410 | * Start a feature/bugfix branch. 411 | * Commit and push until you are happy with your contribution. 412 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 413 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 414 | 415 | ## Copyright 416 | 417 | Copyright (c) 2015 Corey Osman. See LICENSE.txt for 418 | further details. 419 | 420 | 421 | ## Freeipmi Documented Workarounds 422 | 423 | 424 | One of my design goals is to raise exceptions and have the library try workarounds before ultimately failing since there is a whole list of workarounds that can be attempted.However, it would be nice to know the make and model of the server up front to decrease workaround attempts. 425 | 426 | So essentially I need to figure out how to save a command call and then retry if it doesn't work. 427 | 428 | With so many different vendors implementing their own IPMI solutions, different vendors may implement their IPMI protocols incorrectly. The following describes a number of workarounds currently available to handle discovered compliance issues. When possible, workarounds have been implemented so they will be transparent to the user. However, some will require the user to specify a workaround be used via the -W option. 429 | The hardware listed below may only indicate the hardware that a problem was discovered on. Newer versions of hardware may fix the problems indicated below. Similar machines from vendors may or may not exhibit the same problems. Different vendors may license their firmware from the same IPMI firmware developer, so it may be worthwhile to try workarounds listed below even if your motherboard is not listed. 430 | 431 | If you believe your hardware has an additional compliance issue that needs a workaround to be implemented, please contact the FreeIPMI maintainers on or . 432 | 433 | assumeio - This workaround flag will assume inband interfaces communicate with system I/O rather than being memory-mapped. This will work around systems that report invalid base addresses. Those hitting this issue may see "device not supported" or "could not find inband device" errors. Issue observed on HP ProLiant DL145 G1. 434 | 435 | spinpoll - This workaround flag will inform some inband drivers (most notably the KCS driver) to spin while polling rather than putting the process to sleep. This may significantly improve the wall clock running time of tools because an operating system scheduler's granularity may be much larger than the time it takes to perform a single IPMI message transaction. However, by spinning, your system may be performing less useful work by not contexting out the tool for a more useful task. 436 | 437 | authcap - This workaround flag will skip early checks for username capabilities, authentication capabilities, and K_g support and allow IPMI authentication to succeed. It works around multiple issues in which the remote system does not properly report username capabilities, authentication capabilities, or K_g status. Those hitting this issue may see "username invalid", "authentication type unavailable for attempted privilege level", or "k_g invalid" errors. Issue observed on Asus P5M2/P5MT-R/RS162-E4/RX4, Intel SR1520ML/X38ML, and Sun Fire 2200/4150/4450 with ELOM. 438 | 439 | idzero - This workaround flag will allow empty session IDs to be accepted by the client. It works around IPMI sessions that report empty session IDs to the client. Those hitting this issue may see "session timeout" errors. Issue observed on Tyan S2882 with M3289 BMC. 440 | 441 | unexpectedauth - This workaround flag will allow unexpected non-null authcodes to be checked as though they were expected. It works around an issue when packets contain non-null authentication data when they should be null due to disabled per-message authentication. Those hitting this issue may see "session timeout" errors. Issue observed on Dell PowerEdge 2850,SC1425. Confirmed fixed on newer firmware. 442 | 443 | forcepermsg - This workaround flag will force per-message authentication to be used no matter what is advertised by the remote system. It works around an issue when per-message authentication is advertised as disabled on the remote system, but it is actually required for the protocol. Those hitting this issue may see "session timeout" errors. Issue observed on IBM eServer 325. 444 | 445 | endianseq - This workaround flag will flip the endian of the session sequence numbers to allow the session to continue properly. It works around IPMI 1.5 session sequence numbers that are the wrong endian. Those hitting this issue may see "session timeout" errors. Issue observed on some Sun ILOM 1.0/2.0 (depends on service processor endian). 446 | 447 | intel20 - This workaround flag will work around several Intel IPMI 2.0 authentication issues. The issues covered include padding of usernames, and password truncation if the authentication algorithm is HMAC-MD5-128. Those hitting this issue may see "username invalid", "password invalid", or "k_g invalid" errors. Issue observed on Intel SE7520AF2 with Intel Server Management Module (Professional Edition). 448 | 449 | supermicro20 - This workaround flag will work around several Supermicro IPMI 2.0 authentication issues on motherboards w/ Peppercon IPMI firmware. The issues covered include handling invalid length authentication codes. Those hitting this issue may see "password invalid" errors. Issue observed on Supermicro H8QME with SIMSO daughter card. Confirmed fixed on newerver firmware. 450 | 451 | sun20 - This workaround flag will work work around several Sun IPMI 2.0 authentication issues. The issues covered include invalid lengthed hash keys, improperly hashed keys, and invalid cipher suite records. Those hitting this issue may see "password invalid" or "bmc error" errors. Issue observed on Sun Fire 4100/4200/4500 with ILOM. This workaround automatically includes the "opensesspriv" workaround. 452 | 453 | opensesspriv - This workaround flag will slightly alter FreeIPMI's IPMI 2.0 connection protocol to workaround an invalid hashing algorithm used by the remote system. The privilege level sent during the Open Session stage of an IPMI 2.0 connection is used for hashing keys instead of the privilege level sent during the RAKP1 connection stage. Those hitting this issue may see "password invalid", "k_g invalid", or "bad rmcpplus status code" errors. Issue observed on Sun Fire 4100/4200/4500 with ILOM, Inventec 5441/Dell Xanadu II, Supermicro X8DTH, Supermicro X8DTG, Intel S5500WBV/Penguin Relion 700, Intel S2600JF/Appro 512X, and Quanta QSSC-S4R//Appro GB812X-CN. This workaround is automatically triggered with the "sun20" workaround. 454 | 455 | integritycheckvalue - This workaround flag will work around an invalid integrity check value during an IPMI 2.0 session establishment when using Cipher Suite ID 0. The integrity check value should be 0 length, however the remote motherboard responds with a non-empty field. Those hitting this issue may see "k_g invalid" errors. Issue observed on Supermicro X8DTG, Supermicro X8DTU, and Intel S5500WBV/Penguin Relion 700, and Intel S2600JF/Appro 512X. 456 | 457 | No IPMI 1.5 Support - Some motherboards that support IPMI 2.0 have been found to not support IPMI 1.5. Those hitting this issue may see "ipmi 2.0 unavailable" or "connection timeout" errors. This issue can be worked around by using IPMI 2.0 instead of IPMI 1.5 by specifying --driver-address=LAN_2_0. Issue observed on HP Proliant DL 145. 458 | 459 | slowcommit - This workaround will slow down commits to the BMC by sleeping one second between the commit of sections. It works around motherboards that have BMCs that can be overwhelmed by commits. Those hitting this issue may see commit errors or commits not being written to the BMC. Issue observed on Supermicro H8QME. 460 | 461 | veryslowcommit - This workaround will slow down commits to the BMC by sleeping one second between the commit of every key. It works around motherboards that have BMCs that can be overwhelmed by commits. Those hitting this issue may see commit errors or commits not being written to the BMC. Issue observed on Quanta S99Q/Dell FS12-TY. 462 | 463 | 464 | 465 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | ### 0.11.1 2 | * remove hidden files from gem package 3 | 4 | ### 0.11.0 5 | This is a big update with many minor changes and bug fixes. Thank you for all that submitted fixes. 6 | - Removes jeweler dependency and replaces with bundler commands, updates gemspec 7 | - Use HTTPS for homepage field in the gemspec 8 | - Fix infinite loop when executing 'lan print' command 9 | - The retry count counter is currently not incremented, so in the situation 10 | when this command fails, library enters infinite loop of executing this 11 | command, which is bad. 12 | 13 | Adds a missing counter incrementation with the same style as 14 | in basecommands and make it return current @info in case of a failure, 15 | so all methods accessing it will just get return nil. 16 | 17 | - Leverage Enumerable#each_with_object 18 | - Add a SensorsMixin to remove duplicate code 19 | - Enable Rubocop GuardClause and fix complaints 20 | - Remove duplicate methods 21 | - Fixes "NoMethodError: undefined method `success?' for nil:NilClass" 22 | - Some ruby versions need to require 'English' otherwise $CHILD_STATUS is nil 23 | - Fix rubocop Style/MethodName "Use snake_case for method names." 24 | - Rename private method 25 | - Add deprecation warnings for two public methods 26 | - Delete unused method 27 | - Update .rubocop.yml accordingly 28 | - Refactor duplicated chassis power command methods 29 | - Adds additional rubycops 30 | - Fixes many rubycop infractions 31 | - Remove puppet code and vagrant 32 | - Remove if / else logic and unnecessary return in #validate_status 33 | - Reword confusing project description 34 | - Update README.md section on booting to specific devices 35 | - Update documentation around bootpxe, bootdisk functions 36 | - Remove pry statement from method 37 | ### 0.10.0 38 | * gh-26 - make the driver default to lan20 39 | 40 | Users of older IPMI devices will now need to pass the specified driver type. 41 | 42 | ### 0.9.3 43 | * normalize the options being passed into the connect method 44 | 45 | ### 0.9.2 46 | * fixes an issue where is_provider_installed? should only return a boolean value instead of raising and error 47 | * fixes a minor style issue where providers_installed? was returning an array when a boolean might have been expected 48 | 49 | ### 0.9.1 50 | * fixes an issue with connection_works? api call when command raises an error 51 | 52 | ### 0.9.0 53 | * move to rspec3 syntax 54 | * added logging capabilities 55 | * fix freeipmi lan issue with auto driver type not being loaded 56 | * refactor get_diag function to be useful 57 | * remove 1.9.2 support from travis matrix 58 | 59 | ### 0.8.1 60 | * switch to LGPL license 61 | * remove rcov in favor of simplecov 62 | 63 | ### 0.8.0 64 | * changed License from GPL to LGPL 65 | * added option to specify privilge-level 66 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'bundler/gem_tasks' 3 | 4 | @base_dir = File.dirname(__FILE__) 5 | 6 | begin 7 | Bundler.setup(:default, :development) 8 | rescue Bundler::BundlerError => e 9 | $stderr.puts e.message 10 | $stderr.puts "Run `bundle install` to install missing gems" 11 | exit e.status_code 12 | end 13 | require 'rake' 14 | require 'rspec/core' 15 | require 'rspec/core/rake_task' 16 | 17 | desc "run unit tests" 18 | RSpec::Core::RakeTask.new(:unit) do |spec| 19 | spec.pattern = FileList['spec/unit/**/*_spec.rb'] 20 | end 21 | 22 | desc "Run integrations tests against real systems using a vagrant box" 23 | task :vintegration, :user, :pass, :host do |_task, args| 24 | vars = "ipmiuser=#{args[:user]} ipmipass=#{args[:pass]} ipmihost=#{args[:host]}" 25 | puts `cd #{@base_dir}/spec && vagrant up` 26 | puts `cd #{@base_dir}/spec && vagrant provision` 27 | puts `vagrant ssh \"/rubyipmi/rake integration #{vars}\"` 28 | end 29 | 30 | desc "Run integrations tests against real systems" 31 | RSpec::Core::RakeTask.new :integration do |spec| 32 | ENV['ipmiuser'] = 'admin' 33 | ENV['ipmipass'] = 'password' 34 | ENV['ipmihost'] = '10.0.1.16' 35 | providers ||= Array(ENV['ipmiprovider']) || ['freeipmi', 'ipmitool'] 36 | 37 | providers.each do |provider| 38 | ENV['ipmiprovider'] = provider 39 | spec.pattern = FileList['spec/integration/**/*_spec.rb'] 40 | end 41 | end 42 | 43 | task :default => :unit 44 | 45 | require 'rdoc/task' 46 | Rake::RDocTask.new do |rdoc| 47 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 48 | 49 | rdoc.rdoc_dir = 'rdoc' 50 | rdoc.title = "rubyipmi #{version}" 51 | rdoc.rdoc_files.include('README*') 52 | rdoc.rdoc_files.include('lib/**/*.rb') 53 | end 54 | 55 | desc "send diagnostics to logicminds for testing for the given host" 56 | task :send_diag, :user, :pass, :host do |_t, args| 57 | require 'rubyipmi' 58 | require 'net/smtp' 59 | require 'json' 60 | require "highline/import" 61 | 62 | if args.count < 3 63 | raise "You must provide arguments: rake send_diag[user, pass, host]" 64 | end 65 | data = Rubyipmi.get_diag(args[:user], args[:pass], args[:host]) 66 | emailto = 'corey@logicminds.biz' 67 | subject = "Rubyipmi diagnostics data" 68 | send_email(emailto, data.to_json, :subject => subject) 69 | end 70 | 71 | def send_email(to, data, opts = {}) 72 | gmail_id = ask("Enter your gmail account: ") 73 | pass = ask("Enter your gmail password: ") { |q| q.echo = '*' } 74 | opts[:from] = gmail_id 75 | opts[:server] ||= 'smtp.gmail.com' 76 | opts[:from_alias] ||= gmail_id 77 | opts[:subject] ||= @subject 78 | opts[:body] ||= data 79 | opts[:to] ||= to 80 | opts[:port] ||= 587 81 | msg = < 83 | To: <#{to}> 84 | Subject: #{opts[:subject]} 85 | Date: #{Time.now.rfc2822} 86 | 87 | #{opts[:body]} 88 | END_OF_MESSAGE 89 | 90 | smtp = Net::SMTP.new(opts[:server], opts[:port]) 91 | smtp.enable_starttls 92 | smtp.start(opts[:server], gmail_id, pass, :login) do 93 | smtp.send_message(msg, gmail_id, to) 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.10.0 -------------------------------------------------------------------------------- /lib/rubyipmi.rb: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 Corey Osman 2 | # 3 | # This library is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation; either 6 | # version 2.1 of the License, or (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public 14 | # License along with this library; if not, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 16 | # USA 17 | # 18 | 19 | require 'rubyipmi/ipmitool/connection' 20 | require 'rubyipmi/freeipmi/connection' 21 | require 'logger' 22 | require 'open3' 23 | 24 | class NullLogger < Logger 25 | def initialize(*_args) 26 | end 27 | 28 | def add(*_args, &_block) 29 | end 30 | end 31 | 32 | module Rubyipmi 33 | PRIV_TYPES = ['CALLBACK', 'USER', 'OPERATOR', 'ADMINISTRATOR'] 34 | attr_accessor :logger, :log_level 35 | 36 | # set a logger instance yourself to customize where the logs should go 37 | # you will need to set the log level yourself 38 | def self.logger=(log) 39 | @logger = log 40 | end 41 | 42 | # sets the log level, this should be called first if logging to a file is desired 43 | # if you wish to customize the logging options, set the logger yourself with logger= 44 | # valid levels are of the type Logger::INFO, Logger::DEBUG, Logger::ERROR, ... 45 | def self.log_level=(level) 46 | @log_level = level 47 | end 48 | 49 | # this is an read only method that only creates a real logger if the log_level is set 50 | # if the log_level is not setup it creates a null logger which logs nothing 51 | def self.logger 52 | # by default the log will be set to info 53 | unless @logger 54 | if @log_level && @log_level >= 0 55 | @logger = Logger.new('/tmp/rubyipmi.log') 56 | @logger.progname = 'Rubyipmi' 57 | @logger.level = @log_level 58 | else 59 | @logger = NullLogger.new 60 | end 61 | end 62 | @logger 63 | end 64 | 65 | def self.valid_drivers 66 | ['auto', "lan15", "lan20", "open"] 67 | end 68 | 69 | def self.valid_providers 70 | ['auto', 'ipmitool', 'freeipmi'] 71 | end 72 | # The connect method will create a connection object based the provider type passed in 73 | # If provider is left blank the function will use the first available provider 74 | # When the driver is set to auto, rubyipmi will try and figure out which driver to use by common error messages. We will most likely be using 75 | # the lan20 driver, but in order to support a wide use case we default to auto. 76 | def self.connect(user, pass, host, provider = 'any', opts = {:driver => 'lan20', :timeout => 'default'}) 77 | # use this variable to reduce cmd calls 78 | installed = false 79 | 80 | # if the user supplied nil, we want to fix this automatically 81 | opts = {:driver => 'lan20', :timeout => 'default'} if opts.nil? 82 | 83 | # convert all keys to symbols for opts, we can't assume the user will use symbols 84 | opts.keys.each do |key| 85 | opts[(key.to_sym rescue key) || key] = opts.delete(key) 86 | end 87 | 88 | # allow the user to specify an options hash instead of the provider 89 | # in the future I would stop using the provider and use the opts hash instead to get the provider 90 | # This allows us to be a little more flexible if the user is doesn't supply us what we need. 91 | if provider.kind_of?(Hash) 92 | opts = provider 93 | provider = opts[:provider] ||= 'any' 94 | end 95 | 96 | # Verify options just in case user passed in a incomplete hash 97 | opts[:driver] ||= 'lan20' 98 | opts[:timeout] ||= 'default' 99 | 100 | if opts[:privilege] && !supported_privilege_type?(opts[:privilege]) 101 | logger.error("Invalid privilege type :#{opts[:privilege]}, must be one of: #{PRIV_TYPES.join("\n")}") if logger 102 | raise "Invalid privilege type :#{opts[:privilege]}, must be one of: #{PRIV_TYPES.join("\n")}" 103 | end 104 | 105 | # use the first available provider 106 | if provider == 'any' 107 | if is_provider_installed?("freeipmi") 108 | provider = "freeipmi" 109 | installed = true 110 | elsif is_provider_installed?("ipmitool") 111 | provider = "ipmitool" 112 | installed = true 113 | else 114 | logger.error("No IPMI provider is installed, please install freeipmi or ipmitool") 115 | raise "No IPMI provider is installed, please install freeipmi or ipmitool" 116 | end 117 | end 118 | 119 | # Support multiple drivers 120 | # Note: these are just generic names of drivers that need to be specified for each provider 121 | unless valid_drivers.include?(opts[:driver]) 122 | logger.debug("You must specify a valid driver: #{valid_drivers.join(',')}") if logger 123 | raise "You must specify a valid driver: #{valid_drivers.join(',')}" 124 | end 125 | 126 | # If the provider is available create a connection object 127 | if installed || is_provider_installed?(provider) 128 | if provider == "freeipmi" 129 | Rubyipmi::Freeipmi::Connection.new(user, pass, host, opts) 130 | elsif provider == "ipmitool" 131 | Rubyipmi::Ipmitool::Connection.new(user, pass, host, opts) 132 | else 133 | logger.error("Incorrect provider given, must use one of #{valid_providers.join(', ')}") if logger 134 | raise "Incorrect provider given, must use one of #{valid_providers.join(', ')}" 135 | end 136 | else 137 | # Can't find the provider command line tool, maybe try other provider? 138 | logger.error("The IPMI provider: #{provider} is not installed") if logger 139 | raise "The IPMI provider: #{provider} is not installed" 140 | end 141 | end 142 | 143 | # returns boolean true if privilege type is valid 144 | def self.supported_privilege_type?(type) 145 | PRIV_TYPES.include?(type) 146 | end 147 | 148 | # test-friendly capture3 149 | def self.capture3(cmd) 150 | return Open3.capture3(cmd) 151 | end 152 | 153 | # method used to find the command which also makes it easier to mock with 154 | def self.locate_command(commandname) 155 | stdout, stderr, status = Open3.capture3("which #{commandname}") 156 | logger&.error("Which command returned: #{stderr}") unless status.success? 157 | 158 | return nil unless status.success? 159 | stdout 160 | end 161 | 162 | # Return true or false if the provider is available 163 | def self.is_provider_installed?(provider) 164 | case provider 165 | when "freeipmi" 166 | cmdpath = locate_command('ipmipower') 167 | when "ipmitool" 168 | cmdpath = locate_command('ipmitool') 169 | else 170 | logger.error("Invalid BMC provider type #{provider}") if logger 171 | false 172 | end 173 | # return false if command was not found 174 | !cmdpath.nil? 175 | end 176 | 177 | def self.providers 178 | ["freeipmi", "ipmitool"] 179 | end 180 | 181 | # returns true if any of the providers are installed 182 | def self.provider_installed? 183 | providers_installed.length > 0 184 | end 185 | 186 | def self.providers_installed 187 | available = [] 188 | providers.each do |prov| 189 | available << prov if is_provider_installed?(prov) 190 | end 191 | available 192 | end 193 | 194 | # gets data from the bmc device and puts in a hash for diagnostics 195 | def self.get_diag(user, pass, host, opts = {:driver => 'lan20', :timeout => 'default'}) 196 | data = {} 197 | if Rubyipmi.is_provider_installed?('freeipmi') 198 | freeconn = Rubyipmi.connect(user, pass, host, 'freeipmi', opts) 199 | if freeconn 200 | puts "Retrieving freeipmi data" 201 | data[:freeipmi] = freeconn.get_diag 202 | end 203 | end 204 | if Rubyipmi.is_provider_installed?('ipmitool') 205 | ipmiconn = Rubyipmi.connect(user, pass, host, 'ipmitool', opts) 206 | if ipmiconn 207 | puts "Retrieving ipmitool data" 208 | data[:ipmitool] = ipmiconn.get_diag 209 | end 210 | end 211 | File.open('/tmp/rubyipmi_diag_data.txt', 'w') { |f| f.write(data) } 212 | puts "Created file /tmp/rubyipmi_diag_data.txt" 213 | end 214 | end 215 | -------------------------------------------------------------------------------- /lib/rubyipmi/commands/basecommand.rb: -------------------------------------------------------------------------------- 1 | require "observer" 2 | require 'tempfile' 3 | require 'rubyipmi' 4 | 5 | module Rubyipmi 6 | class BaseCommand 7 | include Observable 8 | attr_reader :cmd, :max_retry_count 9 | attr_accessor :options, :passfile 10 | attr_reader :lastcall, :result 11 | 12 | def logger 13 | Rubyipmi.logger 14 | end 15 | 16 | def makecommand 17 | # override in subclass 18 | end 19 | 20 | def setpass 21 | @passfile = Tempfile.new('') 22 | end 23 | 24 | def removepass 25 | @passfile.unlink 26 | end 27 | 28 | def dump_command 29 | makecommand 30 | end 31 | 32 | def initialize(commandname, opts = ObservableHash.new) 33 | # This will locate the command path or raise an error if not found 34 | @cmdname = commandname 35 | @options = opts 36 | @options.add_observer(self) 37 | end 38 | 39 | def locate_command(commandname) 40 | unless location = Rubyipmi.locate_command(commandname) 41 | logger&.error("#{commandname} command not found, is #{commandname} installed?") 42 | raise "#{commandname} command not found, is #{commandname} installed?" 43 | end 44 | location 45 | end 46 | 47 | # Use this function to run the command line tool, it will inherently use the options hash for all options 48 | # That need to be specified on the command line 49 | def runcmd 50 | @success = false 51 | @success = run 52 | logger.debug(@lastcall.inspect) unless @lastcall.nil? if logger 53 | logger.debug(@result) unless @result.nil? if logger 54 | @success 55 | end 56 | 57 | def run 58 | # we search for the command everytime just in case its removed during execution 59 | # we also don't want to add this to the initialize since mocking is difficult and we don't want to 60 | # throw errors upon object creation 61 | retrycount = 0 62 | process_status = false 63 | @cmd = locate_command(@cmdname) 64 | setpass 65 | @result = nil 66 | logger.debug(makecommand) if logger 67 | begin 68 | command = makecommand 69 | @lastcall = command.to_s 70 | @result, @result_err, status = Rubyipmi.capture3(command) 71 | # sometimes the command tool does not return the correct result, validate it with additional code 72 | process_status = validate_status(status) 73 | rescue 74 | if retrycount < max_retry_count 75 | find_fix(@result) 76 | retrycount = retrycount.next 77 | retry 78 | else 79 | logger.error("Exhausted all auto fixes, cannot determine what the problem is") if logger 80 | raise "Exhausted all auto fixes, cannot determine what the problem is" 81 | end 82 | ensure 83 | removepass 84 | process_status 85 | end 86 | end 87 | 88 | # The findfix method acts like a recursive method and applies fixes defined in the errorcodes 89 | # If a fix is found it is applied to the options hash, and then the last run command is retried 90 | # until all the fixes are exhausted or a error not defined in the errorcodes is found 91 | # this must be overrided in the subclass, as there are no generic errors that fit both providers 92 | def find_fix(result) 93 | return unless result 94 | # The errorcode code hash contains the fix 95 | begin 96 | fix = ErrorCodes.search(result) 97 | @options.merge_notify!(fix) 98 | rescue e 99 | raise e 100 | Rubyipmi.logger.debug("Could not find fix for error code: \n#{result}") if logger 101 | raise "Could not find fix for error code: \n#{result}" 102 | end 103 | end 104 | 105 | def update(opts) 106 | @options.merge!(opts) 107 | end 108 | 109 | # This method will check if the results are really valid as the exit code can be misleading and incorrect 110 | def validate_status(exitstatus) 111 | raise "Error occurred" unless exitstatus 112 | raise "Error occurred" if exitstatus.is_a?(Process::Status) && exitstatus.success? 113 | 114 | true 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /lib/rubyipmi/commands/mixins/power_mixin.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi 2 | module PowerMixin 3 | # Turn the system on 4 | def on 5 | command("on") 6 | end 7 | 8 | # Turn the system off 9 | def off 10 | command("off") 11 | end 12 | 13 | # Perform a power reset on the system 14 | def reset 15 | command("reset") 16 | end 17 | 18 | # Power cycle the system 19 | def cycle 20 | off? ? on : command("cycle") 21 | end 22 | 23 | # Perform a soft shutdown, like briefly pushing the power button 24 | def soft_shutdown 25 | command("soft") 26 | end 27 | 28 | # Test to see if the power is on 29 | def on? 30 | status == "on" 31 | end 32 | 33 | # Test to see if the power is off 34 | def off? 35 | status == "off" 36 | end 37 | 38 | # DEPRECATED: Please use soft_shutdown instead. 39 | def softShutdown 40 | warn "[DEPRECATION] `softShutdown` is deprecated, please use `soft_shutdown` instead." 41 | soft_shutdown 42 | end 43 | 44 | # DEPRECATED: Please use power_interrupt instead. 45 | def powerInterrupt 46 | warn "[DEPRECATION] `powerInterrupt` is deprecated, please use `power_interrupt` instead." 47 | power_interrupt 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/rubyipmi/commands/mixins/sensors_mixin.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi 2 | module SensorsMixin 3 | def refresh 4 | @sensors = nil 5 | list 6 | end 7 | 8 | def list 9 | @sensors ||= parse(getsensors) 10 | end 11 | 12 | def count 13 | list.count 14 | end 15 | 16 | def names 17 | list.keys 18 | end 19 | 20 | # returns a hash of fan sensors where the key is fan name and value is the sensor 21 | def fanlist(refreshdata = false) 22 | refresh if refreshdata 23 | list.each_with_object({}) { |(name, sensor), flist| flist[name] = sensor if name =~ /.*fan.*/ } 24 | end 25 | 26 | # returns a hash of sensors where each key is the name of the sensor and the value is the sensor 27 | def templist(refreshdata = false) 28 | refresh if refreshdata 29 | list.each_with_object({}) do |(name, sensor), tlist| 30 | tlist[name] = sensor if (sensor[:unit] =~ /.*degree.*/ || name =~ /.*temp.*/) 31 | end 32 | end 33 | 34 | private 35 | 36 | def method_missing(method, *_args, &_block) 37 | if !list.key?(method.to_s) 38 | raise NoMethodError 39 | else 40 | list[method.to_s] 41 | end 42 | end 43 | 44 | def parse(data) 45 | return {} if data.nil? 46 | 47 | data.lines.each_with_object({}) do |line, sensorlist| 48 | # skip the header 49 | sensor = sensor_class.new(line) 50 | sensorlist[sensor[:name]] = sensor 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/basecommand.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/freeipmi/errorcodes' 2 | 3 | module Rubyipmi::Freeipmi 4 | class BaseCommand < Rubyipmi::BaseCommand 5 | def setpass 6 | super 7 | @options["config-file"] = @passfile.path 8 | @passfile.write "username #{@options['username']}\n" 9 | @passfile.write "password #{@options['password']}\n" 10 | @passfile.close 11 | end 12 | 13 | def max_retry_count 14 | @max_retry_count ||= Rubyipmi::Freeipmi::ErrorCodes.length 15 | end 16 | 17 | def makecommand 18 | # need to format the options to freeipmi format 19 | args = @options.collect do |k, v| 20 | # must remove from command line as its handled via conf file 21 | next if k == 'password' 22 | next if k == 'username' 23 | if !v 24 | "--#{k}" 25 | else 26 | "--#{k}=#{v}" 27 | end 28 | end.join(" ") 29 | 30 | "#{cmd} #{args.rstrip}" 31 | end 32 | 33 | # This method will check if the results are really valid as the exit code can be misleading and incorrect 34 | # this is required because freeipmi in older version always returned 0 even if an error occured 35 | def validate_status(exitstatus) 36 | case @cmdname 37 | when "ipmipower" 38 | # until ipmipower returns the correct exit status this is a hack 39 | # essentially any result greater than 23 characters is an error 40 | raise "Error occurred" if @result.length > 23 41 | when "bmc-config" 42 | if @result.length > 100 && exitstatus.success? 43 | return true 44 | else 45 | raise "Error occurred" 46 | end 47 | else 48 | super 49 | end 50 | end 51 | 52 | # The findfix method acts like a recursive method and applies fixes defined in the errorcodes 53 | # If a fix is found it is applied to the options hash, and then the last run command is retried 54 | # until all the fixes are exhausted or a error not defined in the errorcodes is found 55 | def find_fix(result) 56 | return unless result 57 | # The errorcode code hash contains the fix 58 | begin 59 | fix = ErrorCodes.search(result) 60 | @options.merge_notify!(fix) 61 | rescue 62 | raise "Could not find fix for error code: \n#{result}" 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/bmc.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class Bmc < Rubyipmi::Freeipmi::BaseCommand 3 | # attr_accessor :options 4 | attr_accessor :config 5 | 6 | def initialize(opts = ObservableHash.new) 7 | super("bmc-device", opts) 8 | @bmcinfo = {} 9 | end 10 | 11 | def version 12 | @options['version'] = false 13 | runcmd 14 | @options.delete_notify('version') 15 | @result.slice(/\d\.\d.\d/) 16 | end 17 | 18 | def info 19 | if @bmcinfo.length > 0 20 | @bmcinfo 21 | else 22 | information.retrieve 23 | end 24 | end 25 | 26 | def reset(type = 'cold') 27 | device.reset(type) 28 | end 29 | 30 | def guid 31 | information.guid 32 | end 33 | 34 | def config 35 | @config ||= Rubyipmi::Freeipmi::BmcConfig.new(options) 36 | end 37 | 38 | def lan 39 | @lan ||= Rubyipmi::Freeipmi::Lan.new(options) 40 | end 41 | 42 | def information 43 | @info ||= Rubyipmi::Freeipmi::BmcInfo.new(options) 44 | end 45 | 46 | def device 47 | @bmcdevice ||= Rubyipmi::Freeipmi::BmcDevice.new(options) 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/bmcconfig.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class BmcConfig < Rubyipmi::Freeipmi::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("bmc-config", opts) 5 | @sections = [] 6 | end 7 | 8 | def verbose(on = false) 9 | if on 10 | @options['verbose'] = false 11 | else 12 | @options.delete_notify('verbose') 13 | end 14 | end 15 | 16 | def section(section) 17 | @options["checkout"] = false 18 | @options["section"] = section 19 | runcmd 20 | @options.delete_notify("checkout") 21 | @options.delete_notify("section") 22 | @result 23 | end 24 | 25 | def setsection(section, key, value) 26 | keypair = "#{section}:#{key}=#{value}" 27 | @options["commit"] = false 28 | unless keypair.empty? 29 | @options["key-pair"] = keypair 30 | value = runcmd 31 | @options.delete_notify("commit") 32 | @options.delete_notify("key-pair") 33 | return value 34 | end 35 | false 36 | end 37 | 38 | # returns the entire bmc-config configuration, can take a while to execute 39 | def configuration 40 | @options["checkout"] = false 41 | runcmd 42 | @options.delete_notify("checkout") 43 | @result 44 | end 45 | 46 | # Returns a list of available sections to inspect 47 | def listsections 48 | if @sections.length < 1 49 | @options["listsections"] = false 50 | value = runcmd 51 | @options.delete_notify("listsections") 52 | @sections = @result.split(/\n/) if value 53 | end 54 | @sections 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/bmcdevice.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class BmcDevice < Rubyipmi::Freeipmi::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("bmc-device", opts) 5 | end 6 | 7 | # runs a command like bmc-device --cold-reset 8 | def command(opt) 9 | @options[opt] = false 10 | value = runcmd 11 | @options.delete_notify(opt) 12 | value 13 | end 14 | 15 | # reset the bmc device, useful for debugging and troubleshooting 16 | def reset(type = 'cold') 17 | if ['cold', 'warm'].include?(type) 18 | key = "#{type}-reset" 19 | command(key) 20 | else 21 | logger.error("reset type: #{type} is not a valid choice, use warm or cold") if logger 22 | raise "reset type: #{type} is not a valid choice, use warm or cold" 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/bmcinfo.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class BmcInfo < Rubyipmi::Freeipmi::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("bmc-info", opts) 5 | end 6 | 7 | def guid 8 | options["get-device-guid"] = false 9 | status = runcmd 10 | options.delete_notify("get-device-guid") 11 | if !status 12 | raise @result 13 | else 14 | @result.chomp.strip 15 | end 16 | end 17 | 18 | def retrieve 19 | bmcinfo = {} 20 | status = runcmd 21 | subkey = nil 22 | if !status 23 | raise @result 24 | else 25 | @result.lines.each do |line| 26 | # clean up the data from spaces 27 | item = line.split(':') 28 | key = item.first.strip 29 | value = item.last.strip 30 | # if the following condition is met we have subvalues 31 | if key == value && !subkey 32 | subkey = key 33 | bmcinfo[subkey] = [] 34 | elsif key == value && subkey 35 | # subvalue found 36 | bmcinfo[subkey] << value.gsub(/\[|\]/, "") 37 | else 38 | # Normal key/value pair with no subkeys 39 | subkey = nil 40 | bmcinfo[key] = value 41 | end 42 | end 43 | return bmcinfo 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/chassis.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class Chassis < Rubyipmi::Freeipmi::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("ipmi-chassis", opts) 5 | end 6 | 7 | # Turn the led light on / off or with a delay 8 | def identify(status = false, delay = 0) 9 | if status 10 | if delay <= 0 11 | options["chassis-identify"] = "FORCE" 12 | else 13 | options["chassis-identify"] = delay 14 | end 15 | else 16 | options["chassis-identify"] = "TURN-OFF" 17 | end 18 | # Run the command 19 | value = runcmd 20 | options.delete_notify("chassis-identify") 21 | value 22 | end 23 | 24 | # Access to the power command created on the fly 25 | def power 26 | @power ||= Rubyipmi::Freeipmi::Power.new(@options) 27 | end 28 | 29 | # Access to the config command created on the fly 30 | def config 31 | @config ||= Rubyipmi::Freeipmi::ChassisConfig.new(@options) 32 | end 33 | 34 | # set boot device from given boot device 35 | def bootdevice(device, reboot = false, persistent = false) 36 | if config.bootdevices.include?(device) 37 | bootstatus = config.bootdevice(device, persistent) 38 | power.cycle if reboot && bootstatus 39 | else 40 | logger.error("Device with name: #{device} is not a valid boot device for host #{options['hostname']}") if logger 41 | raise "Device with name: #{device} is not a valid boot device for host #{options['hostname']}" 42 | end 43 | end 44 | 45 | # set boot device to pxe with option to reboot 46 | def bootpxe(reboot = false, persistent = false) 47 | bootstatus = config.bootpxe(persistent) 48 | # Only reboot if setting the boot flag was successful 49 | power.cycle if reboot && bootstatus 50 | end 51 | 52 | # set boot device to disk with option to reboot 53 | def bootdisk(reboot = false, persistent = false) 54 | bootstatus = config.bootdisk(persistent) 55 | # Only reboot if setting the boot flag was successful 56 | power.cycle if reboot && bootstatus 57 | end 58 | 59 | # set boot device to cdrom with option to reboot 60 | def bootcdrom(reboot = false, persistent = false) 61 | bootstatus = config.bootcdrom(persistent) 62 | # Only reboot if setting the boot flag was successful 63 | power.cycle if reboot && bootstatus 64 | end 65 | 66 | # boot into bios setup with option to reboot 67 | def bootbios(reboot = false, persistent = false) 68 | bootstatus = config.bootbios(persistent) 69 | # Only reboot if setting the boot flag was successful 70 | power.cycle if reboot && bootstatus 71 | end 72 | 73 | def status 74 | options["get-status"] = false 75 | value = runcmd 76 | options.delete_notify("get-status") 77 | if value 78 | return parsestatus 79 | else 80 | return value 81 | end 82 | end 83 | 84 | # A currently unsupported method to retrieve the led status 85 | def identifystatus 86 | # TODO: implement this function 87 | # parse out the identify status 88 | # status.result 89 | end 90 | 91 | private 92 | 93 | def parsestatus 94 | statusresult = @result 95 | statusvalues = {} 96 | statusresult.lines.each do |line| 97 | # clean up the data from spaces 98 | item = line.split(':') 99 | key = item.first.strip 100 | value = item.last.strip 101 | statusvalues[key] = value 102 | end 103 | statusvalues 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/chassisconfig.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class ChassisConfig < Rubyipmi::Freeipmi::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("ipmi-chassis-config", opts) 5 | end 6 | 7 | # This is the raw command to send a new configuration to the ipmi device 8 | def commit 9 | @options["commit"] = false 10 | value = runcmd 11 | @options.delete_notify("commit") 12 | value 13 | end 14 | 15 | # This is the raw command to get the entire ipmi chassis configuration 16 | # If you pass in a section you will get just the section 17 | def checkout(section = nil) 18 | @options["checkout"] = false 19 | @options["section"] = section if section 20 | value = runcmd 21 | @options.delete_notify("checkout") 22 | @options.delete_notify("section") if section 23 | value 24 | end 25 | 26 | def bootdevice(device, persistent) 27 | set_boot_flag("Boot_Device", device, persistent) 28 | end 29 | 30 | def bootdevices 31 | # freeipmi returns a list of supported devices 32 | # However, for now we will just assume the following 33 | ["PXE", "HARD-DRIVE", "CD-DVD", "BIOS-SETUP"] 34 | # TODO: return array of possible boot devices 35 | end 36 | 37 | def bootpersistent(value) 38 | # TODO: find out if we can specify multiple key-pair values 39 | if value == true 40 | flag = "Chassis_Boot_Flags:Boot_Flags_Persistent=Yes" 41 | else 42 | flag = "Chassis_Boot_Flags:Boot_Flags_Persistent=No" 43 | end 44 | @options["key-pair"] = "\"#{flag}\"" 45 | value = commit 46 | @options.delete_notify("key-pair") 47 | value 48 | end 49 | 50 | # shortcut to set boot device to pxe 51 | def bootpxe(persistent = true) 52 | bootdevice("PXE", persistent) 53 | end 54 | 55 | # shortcut to set boot device to disk 56 | def bootdisk(persistent = true) 57 | bootdevice("HARD-DRIVE", persistent) 58 | end 59 | 60 | # shortcut to set boot device to cdrom 61 | def bootcdrom(persistent = true) 62 | bootdevice("CD-DVD", persistent) 63 | end 64 | 65 | # shortcut to boot into bios setup 66 | def bootbios(persistent = true) 67 | bootdevice("BIOS-SETUP", persistent) 68 | end 69 | 70 | private 71 | 72 | def set_boot_flag(key, flag, _persistent) 73 | @options["key-pair"] = "\"Chassis_Boot_Flags:#{key}=#{flag}\"" 74 | value = commit 75 | @options.delete_notify("key-pair") 76 | value 77 | end 78 | end 79 | 80 | ## Possible values: NO-OVERRIDE/PXE/HARD-DRIVE/HARD-DRIVE-SAFE-MODE/ 81 | ## DIAGNOSTIC_PARTITION/CD-DVD/BIOS-SETUP/REMOTE-FLOPPY 82 | ## PRIMARY-REMOTE-MEDIA/REMOTE-CD-DVD/REMOTE-HARD-DRIVE/FLOPPY 83 | end 84 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/fru.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class Fru < Rubyipmi::Freeipmi::BaseCommand 3 | attr_accessor :list 4 | 5 | DEFAULT_FRU = 'default_fru_device' 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmi-fru", opts) 9 | @list = {} 10 | end 11 | 12 | def get_from_list(key) 13 | return unless list.key?(DEFAULT_FRU) 14 | list[DEFAULT_FRU][key] if list[DEFAULT_FRU].key?(key) 15 | end 16 | 17 | def manufacturer 18 | get_from_list('board_manufacturer') 19 | end 20 | 21 | def board_serial 22 | get_from_list('board_serial_number') 23 | end 24 | 25 | def serial 26 | get_from_list('board_serial_number') 27 | end 28 | 29 | def model 30 | get_from_list('board_product_name') 31 | end 32 | 33 | # method to retrieve the raw fru data 34 | def getfrus 35 | command 36 | @result 37 | end 38 | 39 | def names 40 | list.keys 41 | end 42 | 43 | # return the list of fru information in a hash 44 | def list 45 | parse(getfrus) if @list.count < 1 46 | @list 47 | end 48 | 49 | private 50 | 51 | def method_missing(method, *_args, &_block) 52 | name = method.to_s 53 | fru = list.fetch(name, nil) 54 | # if the user wanted some data from the default fru, lets show the data for the fru. Otherwise 55 | # we return the Fru with the given name 56 | if fru.nil? 57 | if list[DEFAULT_FRU].keys.include?(name) 58 | return list[DEFAULT_FRU][name] 59 | else 60 | # maybe we should return nil instead? hmm... 61 | raise NoMethodError 62 | end 63 | else 64 | return fru 65 | end 66 | end 67 | 68 | # parse the fru information 69 | def parse(data) 70 | if !data.nil? && !data.empty? 71 | parsed_data = [] 72 | data.lines.each do |line| 73 | if line =~ /^FRU.*/ 74 | # this is the either the first line of of the fru or another fru 75 | if parsed_data.count != 0 76 | # we have reached a new fru device so lets record the previous fru 77 | new_fru = FruData.new(parsed_data) 78 | parsed_data = [] 79 | @list[new_fru[:name]] = new_fru 80 | end 81 | end 82 | parsed_data << line 83 | end 84 | # process the last fru 85 | if parsed_data.count != 0 86 | # we have reached a new fru device so lets record the previous fru 87 | new_fru = FruData.new(parsed_data) 88 | parsed_data = [] 89 | @list[new_fru[:name]] = new_fru 90 | end 91 | end 92 | @list 93 | end 94 | 95 | # run the command and return result 96 | def command 97 | value = runcmd 98 | return @result if value 99 | end 100 | end 101 | 102 | class FruData < Hash 103 | def name 104 | self[:name] 105 | end 106 | 107 | def initialize(data) 108 | parse(data) 109 | end 110 | 111 | # parse the fru information that should be an array of lines 112 | def parse(data) 113 | return unless data 114 | data.each do |line| 115 | key, value = line.split(':', 2) 116 | if key =~ /^FRU.*/ 117 | if value =~ /([\w\s]*)\(.*\)/ 118 | self[:name] = $~[1].strip.gsub(/\ /, '_').downcase 119 | end 120 | else 121 | key = key.strip.gsub(/\ /, '_').downcase.gsub(/fru_/, '') 122 | self[key] = value.strip unless value.nil? 123 | end 124 | end 125 | end 126 | 127 | private 128 | 129 | def method_missing(method, *_args, &_block) 130 | fetch(method.to_s, nil) 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/lan.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class Lan 3 | attr_accessor :info 4 | attr_accessor :channel 5 | attr_accessor :config 6 | 7 | def initialize(opts) 8 | @config = Rubyipmi::Freeipmi::BmcConfig.new(opts) 9 | @info = {} 10 | @channel = 2 11 | end 12 | 13 | def info 14 | if @info.length < 1 15 | @config.verbose(true) 16 | parse(@config.section("Lan_Conf")) 17 | @config.verbose(false) 18 | end 19 | @info 20 | end 21 | 22 | def dhcp? 23 | info.fetch("ip_address_source", nil).match(/dhcp/i) != nil 24 | end 25 | 26 | def static? 27 | info.fetch("ip_address_source", nil).match(/static/i) != nil 28 | end 29 | 30 | def ip 31 | info.fetch("ip_address", nil) 32 | end 33 | 34 | def mac 35 | info.fetch("mac_address", nil) 36 | end 37 | 38 | def netmask 39 | info.fetch("subnet_mask", nil) 40 | end 41 | 42 | def gateway 43 | info.fetch("default_gateway_ip_address", nil) 44 | end 45 | 46 | # def snmp 47 | # 48 | # end 49 | 50 | def vlanid 51 | info.fetch("vlan_id", nil) 52 | # some other vlan configuration that might also be useful 53 | # "vlan_id_enable" 54 | # "vlan_priority" 55 | end 56 | 57 | # def snmp=(community) 58 | # 59 | # end 60 | 61 | # validates that the address, returns true/false 62 | def validaddr?(source) 63 | valid = /^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/.match("#{source}") 64 | if valid.nil? 65 | raise "#{source} is not a valid address" 66 | else 67 | return true 68 | end 69 | end 70 | 71 | def ip=(address) 72 | @config.setsection("Lan_Conf", "IP_Address", address) if validaddr?(address) 73 | end 74 | 75 | def netmask=(netmask) 76 | @config.setsection("Lan_Conf", "Subnet_Mask", netmask) if validaddr?(netmask) 77 | end 78 | 79 | def gateway=(address) 80 | @config.setsection("Lan_Conf", "Default_Gateway_IP_Address", address) if validaddr?(address) 81 | end 82 | 83 | # def vlanid=(vlan) 84 | # 85 | # end 86 | 87 | def parse(landata) 88 | if !landata.nil? && !landata.empty? 89 | landata.lines.each do |line| 90 | # clean up the data from spaces 91 | next if line.match(/#+/) 92 | next if line.match(/Section/i) 93 | line.gsub!(/\t/, '') 94 | item = line.split(/\s+/) 95 | key = item.first.strip.downcase 96 | value = item.last.strip 97 | @info[key] = value 98 | end 99 | end 100 | @info 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/power.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/commands/mixins/power_mixin' 2 | 3 | module Rubyipmi::Freeipmi 4 | class Power < Rubyipmi::Freeipmi::BaseCommand 5 | include Rubyipmi::PowerMixin 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmipower", opts) 9 | end 10 | 11 | # The command function is a wrapper that actually calls the run method 12 | def command(opt) 13 | @options[opt] = false 14 | runcmd 15 | @options.delete_notify(opt) 16 | @result 17 | end 18 | 19 | def power_interrupt 20 | command("pulse") 21 | end 22 | 23 | # Get the power status of the system, will show either on or off 24 | def status 25 | value = command("stat") 26 | @result.split(":").last.chomp.strip if value 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/commands/sensors.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/commands/mixins/sensors_mixin' 2 | 3 | module Rubyipmi::Freeipmi 4 | class Sensors < Rubyipmi::Freeipmi::BaseCommand 5 | include Rubyipmi::SensorsMixin 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmi-sensors", opts) 9 | end 10 | 11 | def getsensors 12 | @options["no-header-output"] = false 13 | @options["output-sensor-state"] = false 14 | @options["entity-sensor-names"] = false 15 | runcmd 16 | @options.delete_notify('no-header-output') 17 | @options.delete_notify('output-sensor-state') 18 | @options.delete_notify('entity-sensor-names') 19 | @result 20 | end 21 | 22 | def sensor_class 23 | Sensor 24 | end 25 | end 26 | 27 | class Sensor < Hash 28 | def initialize(line) 29 | parse(line) 30 | self[:name] = normalize(self[:name]) 31 | end 32 | 33 | private 34 | 35 | def normalize(text) 36 | text.gsub(/\ /, '_').gsub(/\./, '').downcase 37 | end 38 | 39 | # Parse the individual sensor 40 | # Note: not all fields will exist on every server 41 | def parse(line) 42 | fields = [:id_num, :name, :value, :unit, :status, :type, :state, :lower_nonrec, 43 | :lower_crit, :lower_noncrit, :upper_crit, :upper_nonrec, :asserts_enabled, :deasserts_enabled] 44 | data = line.split(/\|/) 45 | # should we ever encounter a field not in the fields list, just use a counter based fieldname so we just 46 | # use field1, field2, field3, ... 47 | i = 0 48 | data.each do |value| 49 | field ||= fields.shift || "field#{i}" 50 | self[field] = value.strip 51 | i = i.next 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/connection.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/freeipmi/errorcodes' 2 | require 'rubyipmi/observablehash' 3 | require 'rubyipmi/commands/basecommand' 4 | require 'rubyipmi/freeipmi/commands/basecommand' 5 | 6 | Dir[File.dirname(__FILE__) + '/commands/*.rb'].each do |file| 7 | require file 8 | end 9 | 10 | module Rubyipmi 11 | module Freeipmi 12 | class Connection 13 | attr_accessor :options 14 | 15 | DRIVERS_MAP = { 16 | 'lan15' => 'LAN', 17 | 'lan20' => 'LAN_2_0', 18 | 'open' => 'OPENIPMI' 19 | } 20 | 21 | def initialize(user, pass, host, opts) 22 | @options = Rubyipmi::ObservableHash.new 23 | raise("Must provide a host to connect to") unless host 24 | @options["hostname"] = host 25 | # Credentials can also be stored in the freeipmi configuration file 26 | # So they are not required 27 | @options["username"] = user if user 28 | @options["password"] = pass if pass 29 | if opts.key?(:privilege) 30 | @options["privilege-level"] = opts[:privilege] # privilege type 31 | end 32 | # Note: rubyipmi should auto detect which driver to use so its unnecessary to specify the driver unless 33 | # the user really wants to 34 | @options['driver-type'] = DRIVERS_MAP[opts[:driver]] unless DRIVERS_MAP[opts[:driver]].nil? 35 | end 36 | 37 | # test the connection to ensure we can at least make a single call 38 | def connection_works? 39 | ! (bmc.info.nil? || bmc.info.empty?) 40 | rescue 41 | false 42 | end 43 | 44 | def provider 45 | 'freeipmi' 46 | end 47 | 48 | def fru 49 | @fru ||= Rubyipmi::Freeipmi::Fru.new(@options) 50 | end 51 | 52 | def bmc 53 | @bmc ||= Rubyipmi::Freeipmi::Bmc.new(@options) 54 | end 55 | 56 | def chassis 57 | @chassis ||= Rubyipmi::Freeipmi::Chassis.new(@options) 58 | end 59 | 60 | def sensors 61 | @sensors ||= Rubyipmi::Freeipmi::Sensors.new(@options) 62 | end 63 | 64 | def get_diag 65 | data = {} 66 | data[:provider] = provider 67 | data[:frus] = fru.getfrus 68 | data[:sensors] = sensors.getsensors 69 | data[:bmc_info] = bmc.info 70 | data[:version] = bmc.version 71 | data 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /lib/rubyipmi/freeipmi/errorcodes.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Freeipmi 2 | class ErrorCodes 3 | CODES = { 4 | "authentication type unavailable for attempted privilege level" => {"driver-type" => "LAN_2_0"}, 5 | "authentication type unavailable for attempted privilege level\n" => {"driver-type" => "LAN_2_0"} 6 | } 7 | 8 | def self.length 9 | CODES.length 10 | end 11 | 12 | def self.code 13 | CODES 14 | end 15 | 16 | def self.search(code) 17 | # example error code: 18 | # "/usr/local/sbin/bmc-info: authentication type unavailable for attempted privilege level\n" 19 | code.chomp! # clean up newline 20 | code = code.split(':').last.strip # clean up left hand side and strip white space from sides 21 | fix = CODES.fetch(code, nil) 22 | if fix.nil? 23 | CODES.each do |error, result| 24 | fix = result if code =~ /.*#{error}.*/i 25 | end 26 | end 27 | raise "No Fix found" if fix.nil? 28 | fix 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/basecommand.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/ipmitool/errorcodes' 2 | 3 | module Rubyipmi::Ipmitool 4 | class BaseCommand < Rubyipmi::BaseCommand 5 | def setpass 6 | super 7 | @options["f"] = @passfile.path 8 | @passfile.write "#{@options['P']}" 9 | @passfile.rewind 10 | @passfile.close 11 | end 12 | 13 | def max_retry_count 14 | @max_retry_count ||= Rubyipmi::Ipmitool::ErrorCodes.length 15 | end 16 | 17 | def makecommand 18 | args = '' 19 | # need to format the options to ipmitool format 20 | @options.each do |k, v| 21 | # must remove from command line as its handled via conf file 22 | next if k == "P" 23 | next if k == "cmdargs" 24 | args << " -#{k} #{v}" 25 | end 26 | 27 | # since ipmitool requires commands to be in specific order 28 | args << ' ' + options.fetch('cmdargs', '') 29 | 30 | "#{cmd} #{args.lstrip}" 31 | end 32 | 33 | # The findfix method acts like a recursive method and applies fixes defined in the errorcodes 34 | # If a fix is found it is applied to the options hash, and then the last run command is retried 35 | # until all the fixes are exhausted or a error not defined in the errorcodes is found 36 | def find_fix(result) 37 | return unless result 38 | # The errorcode code hash contains the fix 39 | begin 40 | fix = ErrorCodes.search(result) 41 | @options.merge_notify!(fix) 42 | 43 | rescue 44 | raise "Could not find fix for error code: \n#{result}" 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/bmc.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Ipmitool 2 | class Bmc < Rubyipmi::Ipmitool::BaseCommand 3 | attr_accessor :config 4 | 5 | def initialize(opts = ObservableHash.new) 6 | super("ipmitool", opts) 7 | @bmcinfo = {} 8 | end 9 | 10 | def lan 11 | @lan ||= Rubyipmi::Ipmitool::Lan.new(@options) 12 | end 13 | 14 | def info 15 | if @bmcinfo.length > 0 16 | @bmcinfo 17 | else 18 | retrieve 19 | end 20 | end 21 | 22 | def version 23 | @options['V'] = nil 24 | runcmd 25 | @options.delete_notify('V') 26 | @result.slice(/\d\.\d.\d/) 27 | end 28 | 29 | # reset the bmc device, useful for troubleshooting 30 | def reset(type = 'cold') 31 | if ['cold', 'warm'].include?(type) 32 | @options["cmdargs"] = "bmc reset #{type}" 33 | value = runcmd 34 | @options.delete_notify("cmdargs") 35 | return value 36 | else 37 | logger.error("reset type: #{type} is not a valid choice, use warm or cold") if logger 38 | raise "reset type: #{type} is not a valid choice, use warm or cold" 39 | end 40 | end 41 | 42 | def guid 43 | @options["cmdargs"] = "bmc guid" 44 | value = runcmd 45 | @options.delete_notify("cmdargs") 46 | return unless value 47 | @result.lines.each do |line| 48 | line.chomp 49 | line.split(":").last.strip if line =~ /GUID/ 50 | end 51 | end 52 | 53 | # This function will get the bmcinfo and return a hash of each item in the info 54 | def retrieve 55 | @options["cmdargs"] = "bmc info" 56 | status = runcmd 57 | @options.delete_notify("cmdargs") 58 | subkey = nil 59 | if !status 60 | raise @result 61 | else 62 | @result.lines.each do |line| 63 | # clean up the data from spaces 64 | item = line.split(':') 65 | key = item.first.strip 66 | value = item.last.strip 67 | # if the following condition is met we have subvalues 68 | if value.empty? 69 | subkey = key 70 | @bmcinfo[subkey] = [] 71 | elsif key == value && subkey 72 | # subvalue found 73 | @bmcinfo[subkey] << value 74 | else 75 | # Normal key/value pair with no subkeys 76 | subkey = nil 77 | @bmcinfo[key] = value 78 | end 79 | end 80 | return @bmcinfo 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/chassis.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Ipmitool 2 | class Chassis < Rubyipmi::Ipmitool::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("ipmitool", opts) 5 | end 6 | 7 | # Turn the led light on / off or with a delay 8 | # status means to enable or disable the blinking 9 | def identify(status = false, delay = 0) 10 | if status 11 | if !delay.between?(1, 255) 12 | options["cmdargs"] = "chassis identify 255" 13 | else 14 | options["cmdargs"] = "chassis identify #{delay}" 15 | end 16 | else 17 | options["cmdargs"] = "chassis identify 0" 18 | end 19 | # Run the command 20 | value = runcmd 21 | options.delete_notify("cmdargs") 22 | value 23 | end 24 | 25 | # Access to the power command created on the fly 26 | def power 27 | @power ||= Rubyipmi::Ipmitool::Power.new(@options) 28 | end 29 | 30 | # Access to the config command created on the fly 31 | def config 32 | @config ||= Rubyipmi::Ipmitool::ChassisConfig.new(@options) 33 | end 34 | 35 | # set boot device from given boot device 36 | def bootdevice(device, reboot = false, persistent = false) 37 | if config.bootdevices.include?(device) 38 | bootstatus = config.bootdevice(device, persistent) 39 | power.cycle if reboot && status 40 | else 41 | logger.debug("Device with name: #{device} is not a valid boot device for host #{options['hostname']}") if logger 42 | raise "Device with name: #{device} is not a valid boot device for host #{options['hostname']}" 43 | end 44 | bootstatus 45 | end 46 | 47 | # set boot device to pxe with option to reboot 48 | def bootpxe(reboot = false, persistent = false) 49 | bootstatus = config.bootpxe(persistent) 50 | # Only reboot if setting the boot flag was successful 51 | power.cycle if reboot && bootstatus 52 | bootstatus 53 | end 54 | 55 | # set boot device to disk with option to reboot 56 | def bootdisk(reboot = false, persistent = false) 57 | bootstatus = config.bootdisk(persistent) 58 | # Only reboot if setting the boot flag was successful 59 | power.cycle if reboot && bootstatus 60 | bootstatus 61 | end 62 | 63 | # set boot device to cdrom with option to reboot 64 | def bootcdrom(reboot = false, persistent = false) 65 | bootstatus = config.bootcdrom(persistent) 66 | # Only reboot if setting the boot flag was successful 67 | power.cycle if reboot && bootstatus 68 | bootstatus 69 | end 70 | 71 | # boot into bios setup with option to reboot 72 | def bootbios(reboot = false, persistent = false) 73 | bootstatus = config.bootbios(persistent) 74 | # Only reboot if setting the boot flag was successful 75 | power.cycle if reboot && bootstatus 76 | bootstatus 77 | end 78 | 79 | def status 80 | options["cmdargs"] = "chassis status" 81 | value = runcmd 82 | options.delete_notify("cmdargs") 83 | {:result => @result, :value => value} 84 | end 85 | 86 | # A currently unsupported method to retrieve the led status 87 | def identifystatus 88 | options["cmdargs"] = "chassis identify status" 89 | value = runcmd 90 | options.delete_notify("cmdargs") 91 | @result.chomp.split(":").last.strip if value 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/chassisconfig.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Ipmitool 2 | class ChassisConfig < Rubyipmi::Ipmitool::BaseCommand 3 | def initialize(opts = ObservableHash.new) 4 | super("ipmitool", opts) 5 | end 6 | 7 | # Set the boot device 8 | def bootdevice(device, persistent = false) 9 | if persistent 10 | @options["cmdargs"] = "chassis bootdev #{device}" 11 | else 12 | @options["cmdargs"] = "chassis bootparam set bootflag force_#{device}" 13 | end 14 | value = runcmd 15 | @options.delete_notify("cmdargs") 16 | value 17 | end 18 | 19 | # Get list of available boot devices 20 | def bootdevices 21 | # ideally we should get this list from the ipmidevice 22 | # However ipmitool only has a static list 23 | ["pxe", "disk", "safe", "diag", "cdrom", "bios", "floppy"] 24 | end 25 | 26 | # shortcut to set boot device to pxe 27 | def bootpxe(persistent = true) 28 | bootdevice("pxe", persistent) 29 | end 30 | 31 | # shortcut to set boot device to disk 32 | def bootdisk(persistent = true) 33 | bootdevice("disk", persistent) 34 | end 35 | 36 | # shortcut to set boot device to cdrom 37 | def bootcdrom(persistent = true) 38 | bootdevice("cdrom", persistent) 39 | end 40 | 41 | # shortcut to boot into bios setup 42 | def bootbios(persistent = true) 43 | bootdevice("bios", persistent) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/fru.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Ipmitool 2 | class Fru < Rubyipmi::Ipmitool::BaseCommand 3 | attr_accessor :list 4 | 5 | DEFAULT_FRU = 'builtin_fru_device' 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmitool", opts) 9 | @list = {} 10 | end 11 | 12 | def names 13 | list.keys 14 | end 15 | 16 | def manufacturer 17 | list[DEFAULT_FRU]['product_manufacturer'] 18 | end 19 | 20 | def serial 21 | list[DEFAULT_FRU]['board_serial'] 22 | end 23 | 24 | def model 25 | list[DEFAULT_FRU]['product_manufacturer'] 26 | end 27 | 28 | # return the list of fru information in a hash 29 | def list 30 | parse(getfrus) if @list.count < 1 31 | @list 32 | end 33 | 34 | # method to retrieve the raw fru data 35 | def getfrus 36 | command 37 | end 38 | 39 | private 40 | 41 | # I use method missing to allow the user to say Fru. which returns a frudata object unless the user 42 | # passes a keyname from the default fru device 43 | def method_missing(method, *_args, &_block) 44 | name = method.to_s 45 | fru = list.fetch(name, nil) 46 | # if the user wanted some data from the default fru, lets show the data for the fru. Otherwise 47 | # we return the Fru with the given name 48 | if fru.nil? 49 | if list[DEFAULT_FRU].keys.include?(name) 50 | return list[DEFAULT_FRU][name] 51 | else 52 | # maybe we should return nil instead? hmm... 53 | raise NoMethodError 54 | end 55 | else 56 | return fru 57 | end 58 | end 59 | 60 | # parse the fru information 61 | def parse(data) 62 | return unless data 63 | parsed_data = [] 64 | data.lines.each do |line| 65 | if line =~ /^FRU.*/ 66 | # this is the either the first line of of the fru or another fru 67 | if parsed_data.count != 0 68 | # we have reached a new fru device so lets record the previous fru 69 | new_fru = FruData.new(parsed_data) 70 | parsed_data = [] 71 | @list[new_fru[:name]] = new_fru 72 | end 73 | 74 | end 75 | parsed_data << line 76 | end 77 | # process the last fru 78 | return if parsed_data.count == 0 79 | # we have reached a new fru device so lets record the previous fru 80 | new_fru = FruData.new(parsed_data) 81 | parsed_data = [] 82 | @list[new_fru[:name]] = new_fru 83 | end 84 | 85 | # run the command and return result 86 | def command 87 | @options["cmdargs"] = "fru" 88 | value = runcmd 89 | @options.delete_notify("cmdargs") 90 | return @result if value 91 | end 92 | end 93 | 94 | class FruData < Hash 95 | def name 96 | self[:name] 97 | end 98 | 99 | def initialize(data) 100 | parse(data) 101 | end 102 | 103 | # parse the fru information that should be an array of lines 104 | def parse(data) 105 | return unless data 106 | data.each do |line| 107 | key, value = line.split(':', 2) 108 | if key =~ /^FRU\s+Device.*/ 109 | if value =~ /([\w\s]*)\(.*\)/ 110 | self[:name] = $~[1].strip.gsub(/\ /, '_').downcase 111 | end 112 | else 113 | key = key.strip.gsub(/\ /, '_').downcase 114 | self[key] = value.strip unless value.nil? 115 | end 116 | end 117 | end 118 | 119 | private 120 | 121 | def method_missing(method, *_args, &_block) 122 | fetch(method.to_s, nil) 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/lan.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi::Ipmitool 2 | class Lan < Rubyipmi::Ipmitool::BaseCommand 3 | attr_accessor :info 4 | attr_accessor :channel 5 | MAX_RETRY = 1 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmitool", opts) 9 | @info = {} 10 | @channel = 2 11 | end 12 | 13 | # sets the info var to be empty causing the variable to repopulate upon the next call to info 14 | def refresh 15 | @info = {} 16 | end 17 | 18 | def channel=(num) 19 | refresh 20 | @channel = num 21 | end 22 | 23 | def info 24 | retrycount = 0 25 | if @info.length < 1 26 | begin 27 | parse(print) 28 | rescue 29 | # sometimes we need to get the info from channel 1, 30 | # wait for error to occur then retry using channel 1 31 | if retrycount < MAX_RETRY 32 | @channel = 1 33 | retrycount = retrycount.next 34 | retry 35 | else 36 | # failed to fetch info, return cached info 37 | @info 38 | end 39 | end 40 | else 41 | # return the cached info 42 | @info 43 | end 44 | end 45 | 46 | def snmp 47 | info.fetch("snmp_community_string", nil) 48 | end 49 | 50 | def ip 51 | info.fetch("ip_address", nil) 52 | end 53 | 54 | def mac 55 | info.fetch("mac_address", nil) 56 | end 57 | 58 | def netmask 59 | info.fetch("subnet_mask", nil) 60 | end 61 | 62 | def gateway 63 | info.fetch("default_gateway_ip", nil) 64 | end 65 | 66 | def vlanid 67 | info.fetch("802.1q_vlan_id", nil) 68 | end 69 | 70 | # def snmp=(community) 71 | # @options["cmdargs"] = "lan set #{channel} snmp #{community}" 72 | # value = runcmd 73 | # @options.delete_notify("cmdargs") 74 | # return value 75 | # end 76 | 77 | def ip=(address) 78 | @options["cmdargs"] = "lan set #{channel} ipaddr #{address}" 79 | value = runcmd 80 | @options.delete_notify("cmdargs") 81 | value 82 | end 83 | 84 | def netmask=(mask) 85 | @options["cmdargs"] = "lan set #{channel} netmask #{mask}" 86 | value = runcmd 87 | @options.delete_notify("cmdargs") 88 | value 89 | end 90 | 91 | def gateway=(address) 92 | @options["cmdargs"] = "lan set #{channel} defgw ipaddr #{address}" 93 | value = runcmd 94 | @options.delete_notify("cmdargs") 95 | value 96 | end 97 | 98 | def dhcp? 99 | info.fetch("ip_address_source", nil).match(/dhcp/i) != nil 100 | end 101 | 102 | def static? 103 | info.fetch("ip_address_source", nil).match(/static/i) != nil 104 | end 105 | 106 | def vlanid=(vlan) 107 | @options["cmdargs"] = "lan set #{channel} vlan id #{vlan}" 108 | value = runcmd 109 | @options.delete_notify("cmdargs") 110 | value 111 | end 112 | 113 | private 114 | 115 | def print 116 | @options["cmdargs"] = "lan print" 117 | value = runcmd 118 | @options.delete_notify("cmdargs") 119 | @result if value 120 | end 121 | 122 | def parse(landata) 123 | landata.lines.each do |line| 124 | # clean up the data from spaces 125 | item = line.split(':', 2) 126 | key = normalize(item.first.strip) 127 | value = item.last.strip 128 | @info[key] = value 129 | end 130 | @info 131 | end 132 | 133 | def normalize(text) 134 | text.gsub(/\ /, '_').gsub(/\./, '').downcase 135 | end 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/power.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/commands/mixins/power_mixin' 2 | 3 | module Rubyipmi::Ipmitool 4 | class Power < Rubyipmi::Ipmitool::BaseCommand 5 | include Rubyipmi::PowerMixin 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmitool", opts) 9 | end 10 | 11 | # The command function is a wrapper that actually calls the run method 12 | def command(opt) 13 | @options["cmdargs"] = "power #{opt}" 14 | value = runcmd 15 | @options.delete_notify("cmdargs") 16 | value 17 | end 18 | 19 | def power_interrupt 20 | command("diag") 21 | end 22 | 23 | # Get the power status of the system, will show either on or off 24 | def status 25 | value = command("status") 26 | @result.match(/(off|on)/).to_s if value 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/commands/sensors.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/commands/mixins/sensors_mixin' 2 | 3 | module Rubyipmi::Ipmitool 4 | class Sensors < Rubyipmi::Ipmitool::BaseCommand 5 | include Rubyipmi::SensorsMixin 6 | 7 | def initialize(opts = ObservableHash.new) 8 | super("ipmitool", opts) 9 | end 10 | 11 | def getsensors 12 | options["cmdargs"] = "sensor" 13 | runcmd 14 | options.delete_notify("cmdargs") 15 | @result 16 | end 17 | 18 | def sensor_class 19 | Sensor 20 | end 21 | end 22 | 23 | class Sensor < Hash 24 | def initialize(line) 25 | parse(line) 26 | self[:name] = normalize(self[:name]) 27 | end 28 | 29 | private 30 | 31 | def normalize(text) 32 | text.gsub(/\ /, '_').gsub(/\./, '').downcase 33 | end 34 | 35 | # Parse the individual sensor 36 | # Note: not all fields will exist on every server 37 | def parse(line) 38 | fields = [:name, :value, :unit, :status, :type, :state, :lower_nonrec, 39 | :lower_crit, :lower_noncrit, :upper_crit, :upper_nonrec, :asserts_enabled, :deasserts_enabled] 40 | # skip the header 41 | data = line.split(/\|/) 42 | # should we ever encounter a field not in the fields list, just use a counter based fieldname 43 | i = 0 44 | data.each do |value| 45 | field ||= fields.shift || "field#{i}" 46 | self[field] = value.strip 47 | i = i.next 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/connection.rb: -------------------------------------------------------------------------------- 1 | require 'rubyipmi/ipmitool/errorcodes' 2 | require 'rubyipmi/observablehash' 3 | require 'rubyipmi/commands/basecommand' 4 | require 'rubyipmi/ipmitool/commands/basecommand' 5 | 6 | Dir[File.dirname(__FILE__) + '/commands/*.rb'].each do |file| 7 | require file 8 | end 9 | 10 | module Rubyipmi 11 | module Ipmitool 12 | class Connection 13 | attr_accessor :options 14 | 15 | DRIVERS_MAP = { 16 | 'lan15' => 'lan', 17 | 'lan20' => 'lanplus', 18 | 'open' => 'open' 19 | } 20 | 21 | def initialize(user, pass, host, opts) 22 | @options = Rubyipmi::ObservableHash.new 23 | raise("Must provide a host to connect to") unless host 24 | @options["H"] = host 25 | # Credentials can also be stored in the freeipmi configuration file 26 | # So they are not required 27 | @options["U"] = user if user 28 | @options["P"] = pass if pass 29 | @options["L"] = opts[:privilege] if opts.key?(:privilege) 30 | # Note: rubyipmi should auto detect which driver to use so its unnecessary to specify the driver unless 31 | # the user really wants to. 32 | @options['I'] = DRIVERS_MAP[opts[:driver]] unless DRIVERS_MAP[opts[:driver]].nil? 33 | end 34 | 35 | # test the connection to ensure we can at least make a single call 36 | def connection_works? 37 | ! (bmc.info.nil? || bmc.info.empty?) 38 | rescue 39 | false 40 | end 41 | 42 | def fru 43 | @fru ||= Rubyipmi::Ipmitool::Fru.new(@options) 44 | end 45 | 46 | def provider 47 | 'ipmitool' 48 | end 49 | 50 | def bmc 51 | @bmc ||= Rubyipmi::Ipmitool::Bmc.new(@options) 52 | end 53 | 54 | def sensors 55 | @sensors ||= Rubyipmi::Ipmitool::Sensors.new(@options) 56 | end 57 | 58 | def chassis 59 | @chassis ||= Rubyipmi::Ipmitool::Chassis.new(@options) 60 | end 61 | 62 | def get_diag 63 | data = {} 64 | data[:provider] = provider 65 | data[:frus] = fru.getfrus 66 | data[:sensors] = sensors.getsensors 67 | data[:bmc_info] = bmc.info 68 | data[:version] = bmc.version 69 | data 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/rubyipmi/ipmitool/errorcodes.rb: -------------------------------------------------------------------------------- 1 | module Rubyipmi 2 | module Ipmitool 3 | class ErrorCodes 4 | CODES = { 5 | "Authentication type NONE not supported\nAuthentication type NONE not supported\n" \ 6 | "Error: Unable to establish LAN session\nGet Device ID command failed\n" => {"I" => "lanplus"}, 7 | "Authentication type NONE not supported" => {"I" => "lanplus"}, 8 | "Error: Unable to establish LAN session\nGet Device ID command failed\n" => {"I" => "lanplus"} 9 | } 10 | 11 | def self.length 12 | CODES.length 13 | end 14 | 15 | def self.code 16 | CODES 17 | end 18 | 19 | def self.search(code) 20 | fix = CODES.fetch(code, nil) 21 | if fix.nil? 22 | CODES.each do |error, result| 23 | # the error should be a subset of the actual erorr 24 | return result if code =~ /.*#{error}.*/i 25 | end 26 | else 27 | return fix 28 | end 29 | raise "No Fix found" if fix.nil? 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/rubyipmi/observablehash.rb: -------------------------------------------------------------------------------- 1 | require "observer" 2 | 3 | module Rubyipmi 4 | class ObservableHash < Hash 5 | include Observable 6 | 7 | # this method will merge the hash and then notify all observers, if any 8 | def merge_notify!(newhash) 9 | merge!(newhash) 10 | changed 11 | notify_observers(self) 12 | end 13 | 14 | def delete_notify(del) 15 | delete(del) 16 | notify_observers(self) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rubyipmi/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Rubyipmi 4 | VERSION = '0.11.1'.freeze 5 | end 6 | -------------------------------------------------------------------------------- /rubyipmi.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('lib', __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'rubyipmi/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "rubyipmi" 7 | s.version = Rubyipmi::VERSION 8 | 9 | s.authors = ["Corey Osman"] 10 | s.date = "2021-09-29" 11 | s.description = "Controls IPMI devices via command line wrapper for ipmitool and freeipmi" 12 | s.email = "corey@logicminds.biz" 13 | s.extra_rdoc_files = [ 14 | "LICENSE.txt", 15 | "README.md" 16 | ] 17 | s.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.match(/^(\.|test|spec|features)/) || f.match(/^*.tar\.gz/) 19 | end 20 | s.homepage = "https://github.com/logicminds/rubyipmi" 21 | s.license = "LGPL-2.1-only" 22 | s.summary = "A ruby wrapper for ipmi command line tools that supports ipmitool and freeipmi" 23 | s.require_paths = ['lib'] 24 | s.add_dependency 'observer', '~> 0.1.0' 25 | s.add_development_dependency 'rspec' 26 | s.add_development_dependency 'rdoc', "~> 3.12" 27 | s.add_development_dependency 'bundler', "~> 2.0" 28 | s.add_development_dependency 'highline', '>= 1.0', '< 3' 29 | s.add_development_dependency 'rake', '~> 13' 30 | end 31 | -------------------------------------------------------------------------------- /spec/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "centos6.4" 9 | config.vm.box_url = "http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box" 10 | config.ssh.private_key_path = "vagrant" 11 | config.vm.provider :virtualbox do |_vb, _override| 12 | # vb.gui = true 13 | end 14 | config.vm.provider :vmware_fusion do |_vf, override| 15 | override.vm.box_url = "https://dl.dropbox.com/u/5721940/vagrant-boxes/vagrant-centos-6.4-x86_64-vmware_fusion.box" 16 | # vf.gui = true 17 | end 18 | 19 | # Boot with a GUI so you can see the screen. (Default is headless) 20 | # config.vm.boot_mode = :gui 21 | 22 | # Enable provisioning with Puppet stand alone. Puppet manifests 23 | # are contained in a directory path relative to this Vagrantfile. 24 | # You will need to create the manifests directory and a manifest in 25 | # the file centos.pp in the manifests_path directory. 26 | # 27 | config.vm.synced_folder "../", "/rubyipmi" 28 | (1..1).each do |i| 29 | vmname = "testnode#{i}" 30 | config.vm.define vmname.to_sym do |web_conf| 31 | web_conf.vm.hostname = vmname 32 | web_conf.vm.provision :shell, 33 | :inline => "hostname #{vmname}.company.corp" 34 | web_conf.vm.provision :puppet do |puppet| 35 | puppet.module_path = "puppetmodules" 36 | puppet.manifests_path = "manifests" 37 | puppet.manifest_file = "default.pp" 38 | puppet.facter = {} 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/bmc_config.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Section UserX Comments 3 | # 4 | # In the following User sections, users should configure usernames, passwords, 5 | # and access rights for IPMI over LAN communication. Usernames can be set to any 6 | # string with the exception of User1, which is a fixed to the "anonymous" 7 | # username in IPMI. 8 | # 9 | # For IPMI over LAN access for a username, set "Enable_User" to "Yes", 10 | # "Lan_Enable_IPMI_Msgs" to "Yes", and "Lan_Privilege_Limit" to a privilege 11 | # level. The privilege level is used to limit various IPMI operations for 12 | # individual usernames. It is recommened that atleast one username be created 13 | # with a privilege limit "Administrator", so all system functions are available 14 | # to atleast one username via IPMI over LAN. For security reasons, we recommend 15 | # not enabling the "anonymous" User1. For most users, "Lan_Session_Limit" can be 16 | # set to 0 (or ignored) to support an unlimited number of simultaneous IPMI over 17 | # LAN sessions. 18 | # 19 | # If your system supports IPMI 2.0 and Serial-over-LAN (SOL), 20 | # a"SOL_Payload_Access" field may be listed below. Set the "SOL_Payload_Access" 21 | # field to "Yes" or "No" to enable or disable this username's ability to access 22 | # SOL. 23 | # 24 | # Please do not forget to uncomment those fields, such as "Password", that may 25 | # be commented out during the checkout. 26 | # 27 | # Some motherboards may require a "Username" to be configured prior to other 28 | # fields being read/written. If this is the case, those fields will be set to 29 | # . 30 | # 31 | Section User1 32 | ## Give Username 33 | ## Username NULL 34 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 35 | ## Password 36 | ## Possible values: Yes/No or blank to not set 37 | Enable_User Yes 38 | ## Possible values: Yes/No 39 | Lan_Enable_IPMI_Msgs Yes 40 | ## Possible values: Yes/No 41 | Lan_Enable_Link_Auth No 42 | ## Possible values: Yes/No 43 | Lan_Enable_Restricted_to_Callback No 44 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 45 | Lan_Privilege_Limit Administrator 46 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 47 | ## Lan_Session_Limit 48 | EndSection 49 | Section User2 50 | ## Give Username 51 | Username admin 52 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 53 | ## Password 54 | ## Possible values: Yes/No or blank to not set 55 | Enable_User Yes 56 | ## Possible values: Yes/No 57 | Lan_Enable_IPMI_Msgs Yes 58 | ## Possible values: Yes/No 59 | Lan_Enable_Link_Auth No 60 | ## Possible values: Yes/No 61 | Lan_Enable_Restricted_to_Callback No 62 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 63 | Lan_Privilege_Limit Administrator 64 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 65 | ## Lan_Session_Limit 66 | EndSection 67 | Section User3 68 | ## Give Username 69 | Username admin3 70 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 71 | ## Password 72 | ## Possible values: Yes/No or blank to not set 73 | Enable_User No 74 | ## Possible values: Yes/No 75 | Lan_Enable_IPMI_Msgs No 76 | ## Possible values: Yes/No 77 | Lan_Enable_Link_Auth No 78 | ## Possible values: Yes/No 79 | Lan_Enable_Restricted_to_Callback No 80 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 81 | Lan_Privilege_Limit No_Access 82 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 83 | ## Lan_Session_Limit 84 | EndSection 85 | Section User4 86 | ## Give Username 87 | Username (Empty User) 88 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 89 | ## Password 90 | ## Possible values: Yes/No or blank to not set 91 | Enable_User No 92 | ## Possible values: Yes/No 93 | Lan_Enable_IPMI_Msgs No 94 | ## Possible values: Yes/No 95 | Lan_Enable_Link_Auth No 96 | ## Possible values: Yes/No 97 | Lan_Enable_Restricted_to_Callback No 98 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 99 | Lan_Privilege_Limit No_Access 100 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 101 | ## Lan_Session_Limit 102 | EndSection 103 | Section User5 104 | ## Give Username 105 | Username (Empty User) 106 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 107 | ## Password 108 | ## Possible values: Yes/No or blank to not set 109 | Enable_User No 110 | ## Possible values: Yes/No 111 | Lan_Enable_IPMI_Msgs No 112 | ## Possible values: Yes/No 113 | Lan_Enable_Link_Auth No 114 | ## Possible values: Yes/No 115 | Lan_Enable_Restricted_to_Callback No 116 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 117 | Lan_Privilege_Limit No_Access 118 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 119 | ## Lan_Session_Limit 120 | EndSection 121 | Section User6 122 | ## Give Username 123 | Username (Empty User) 124 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 125 | ## Password 126 | ## Possible values: Yes/No or blank to not set 127 | Enable_User No 128 | ## Possible values: Yes/No 129 | Lan_Enable_IPMI_Msgs No 130 | ## Possible values: Yes/No 131 | Lan_Enable_Link_Auth No 132 | ## Possible values: Yes/No 133 | Lan_Enable_Restricted_to_Callback No 134 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 135 | Lan_Privilege_Limit No_Access 136 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 137 | ## Lan_Session_Limit 138 | EndSection 139 | Section User7 140 | ## Give Username 141 | Username (Empty User) 142 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 143 | ## Password 144 | ## Possible values: Yes/No or blank to not set 145 | Enable_User No 146 | ## Possible values: Yes/No 147 | Lan_Enable_IPMI_Msgs No 148 | ## Possible values: Yes/No 149 | Lan_Enable_Link_Auth No 150 | ## Possible values: Yes/No 151 | Lan_Enable_Restricted_to_Callback No 152 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 153 | Lan_Privilege_Limit No_Access 154 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 155 | ## Lan_Session_Limit 156 | EndSection 157 | Section User8 158 | ## Give Username 159 | Username (Empty User) 160 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 161 | ## Password 162 | ## Possible values: Yes/No or blank to not set 163 | Enable_User No 164 | ## Possible values: Yes/No 165 | Lan_Enable_IPMI_Msgs No 166 | ## Possible values: Yes/No 167 | Lan_Enable_Link_Auth No 168 | ## Possible values: Yes/No 169 | Lan_Enable_Restricted_to_Callback No 170 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 171 | Lan_Privilege_Limit No_Access 172 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 173 | ## Lan_Session_Limit 174 | EndSection 175 | Section User9 176 | ## Give Username 177 | Username (Empty User) 178 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 179 | ## Password 180 | ## Possible values: Yes/No or blank to not set 181 | Enable_User No 182 | ## Possible values: Yes/No 183 | Lan_Enable_IPMI_Msgs No 184 | ## Possible values: Yes/No 185 | Lan_Enable_Link_Auth No 186 | ## Possible values: Yes/No 187 | Lan_Enable_Restricted_to_Callback No 188 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 189 | Lan_Privilege_Limit No_Access 190 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 191 | ## Lan_Session_Limit 192 | EndSection 193 | Section User10 194 | ## Give Username 195 | Username (Empty User) 196 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 197 | ## Password 198 | ## Possible values: Yes/No or blank to not set 199 | Enable_User No 200 | ## Possible values: Yes/No 201 | Lan_Enable_IPMI_Msgs No 202 | ## Possible values: Yes/No 203 | Lan_Enable_Link_Auth No 204 | ## Possible values: Yes/No 205 | Lan_Enable_Restricted_to_Callback No 206 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 207 | Lan_Privilege_Limit No_Access 208 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 209 | ## Lan_Session_Limit 210 | EndSection 211 | Section User11 212 | ## Give Username 213 | Username (Empty User) 214 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 215 | ## Password 216 | ## Possible values: Yes/No or blank to not set 217 | Enable_User No 218 | ## Possible values: Yes/No 219 | Lan_Enable_IPMI_Msgs No 220 | ## Possible values: Yes/No 221 | Lan_Enable_Link_Auth No 222 | ## Possible values: Yes/No 223 | Lan_Enable_Restricted_to_Callback No 224 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 225 | Lan_Privilege_Limit No_Access 226 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 227 | ## Lan_Session_Limit 228 | EndSection 229 | Section User12 230 | ## Give Username 231 | Username (Empty User) 232 | ## Give password or blank to clear. MAX 16 chars (20 chars if IPMI 2.0 supported). 233 | ## Password 234 | ## Possible values: Yes/No or blank to not set 235 | Enable_User No 236 | ## Possible values: Yes/No 237 | Lan_Enable_IPMI_Msgs No 238 | ## Possible values: Yes/No 239 | Lan_Enable_Link_Auth No 240 | ## Possible values: Yes/No 241 | Lan_Enable_Restricted_to_Callback No 242 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary/No_Access 243 | Lan_Privilege_Limit No_Access 244 | ## Possible values: 0-17, 0 is unlimited; May be reset to 0 if not specified 245 | ## Lan_Session_Limit 246 | EndSection 247 | # 248 | # Section Lan_Channel Comments 249 | # 250 | # In the Lan_Channel section, general IPMI over LAN can be enabled for disabled. 251 | # In the below, "Volatile" configurations are immediately configured onto the 252 | # BMC and will have immediate effect on the system. "Non_Volatile" 253 | # configurations are only available after the next system reset. Generally, both 254 | # the "Volatile" and "Non_Volatile" equivalent fields should be configured 255 | # identically. 256 | # 257 | # To enable IPMI over LAN, typically "Access_Mode" should be set to 258 | # "Always_Available". "Channel_Privilege_Limit" should be set to the highest 259 | # privilege level any username was configured with. Typically, this is set to 260 | # "Administrator". 261 | # 262 | # "User_Level_Auth" and "Per_Message_Auth" are typically set to "Yes" for 263 | # additional security. 264 | # 265 | Section Lan_Channel 266 | ## Possible values: Disabled/Pre_Boot_Only/Always_Available/Shared 267 | Volatile_Access_Mode Always_Available 268 | ## Possible values: Yes/No 269 | Volatile_Enable_User_Level_Auth Yes 270 | ## Possible values: Yes/No 271 | Volatile_Enable_Per_Message_Auth No 272 | ## Possible values: Yes/No 273 | Volatile_Enable_Pef_Alerting No 274 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary 275 | Volatile_Channel_Privilege_Limit Administrator 276 | ## Possible values: Disabled/Pre_Boot_Only/Always_Available/Shared 277 | Non_Volatile_Access_Mode Always_Available 278 | ## Possible values: Yes/No 279 | Non_Volatile_Enable_User_Level_Auth Yes 280 | ## Possible values: Yes/No 281 | Non_Volatile_Enable_Per_Message_Auth No 282 | ## Possible values: Yes/No 283 | Non_Volatile_Enable_Pef_Alerting No 284 | ## Possible values: Callback/User/Operator/Administrator/OEM_Proprietary 285 | Non_Volatile_Channel_Privilege_Limit Administrator 286 | EndSection 287 | # 288 | # Section Lan_Conf Comments 289 | # 290 | # In the Lan_Conf section, typical networking configuration is setup. Most users 291 | # will choose to set "Static" for the "IP_Address_Source" and set the 292 | # appropriate "IP_Address", "MAC_Address", "Subnet_Mask", etc. for the machine. 293 | # 294 | Section Lan_Conf 295 | ## Possible values: Unspecified/Static/Use_DHCP/Use_BIOS/Use_Others 296 | IP_Address_Source Use_DHCP 297 | ## Give valid IP address 298 | IP_Address 192.168.1.24 299 | ## Give valid MAC address 300 | MAC_Address 00:17:A4:49:AB:70 301 | ## Give valid Subnet Mask 302 | Subnet_Mask 255.255.255.0 303 | ## Give valid IP address 304 | Default_Gateway_IP_Address 192.168.1.1 305 | EndSection 306 | # 307 | # Section Lan_Conf_Auth Comments 308 | # 309 | # In the Lan_Conf_Auth section, allowable authentication mechanisms for IPMI 1.5 310 | # is configured. Most users will want to set all "MD5" authentication to "Yes" 311 | # and the rest to "No". If you have configured a NULL username and a NULL 312 | # password, you will also want to configure some of the "None" fields to "Yes" 313 | # to allow "None" authentication to work. Some motherboards do not allow you to 314 | # enable OEM authentication, so you may wish to set all OEM related fields to 315 | # "No". 316 | # 317 | Section Lan_Conf_Auth 318 | -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/bmc_config_lan_conf.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Section Lan_Conf Comments 3 | # 4 | # In the Lan_Conf section, typical networking configuration is setup. Most users 5 | # will choose to set "Static" for the "IP_Address_Source" and set the 6 | # appropriate "IP_Address", "MAC_Address", "Subnet_Mask", etc. for the machine. 7 | # 8 | Section Lan_Conf 9 | ## Possible values: Unspecified/Static/Use_DHCP/Use_BIOS/Use_Others 10 | IP_Address_Source Use_DHCP 11 | ## Give valid IP address 12 | IP_Address 192.168.1.24 13 | ## Give valid MAC address 14 | MAC_Address 00:17:A4:49:AB:70 15 | ## Give valid Subnet Mask 16 | Subnet_Mask 255.255.255.0 17 | ## Give valid IP address 18 | Default_Gateway_IP_Address 192.168.1.1 19 | EndSection 20 | -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/bmc_info.txt: -------------------------------------------------------------------------------- 1 | Device ID : 17 2 | Device Revision : 1 3 | Device SDRs : supported 4 | Firmware Revision : 2.09 5 | Device Available : yes (normal operation) 6 | IPMI Version : 2.0 7 | Sensor Device : supported 8 | SDR Repository Device : supported 9 | SEL Device : supported 10 | FRU Inventory Device : supported 11 | IPMB Event Receiver : unsupported 12 | IPMB Event Generator : unsupported 13 | Bridge : unsupported 14 | Chassis Device : unsupported 15 | Manufacturer ID : Hewlett-Packard (11) 16 | Product ID : 8192 17 | 18 | Channel Information 19 | 20 | Channel Number : 2 21 | Medium Type : 802.3 LAN 22 | Protocol Type : IPMB-1.0 23 | Active Session Count : 0 24 | Session Support : multi-session 25 | Vendor ID : Intelligent Platform Management Interface forum (7154) 26 | 27 | Channel Number : 7 28 | Medium Type : OEM 29 | Protocol Type : KCS 30 | Active Session Count : 0 31 | Session Support : session-less 32 | Vendor ID : Intelligent Platform Management Interface forum (7154) 33 | -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/errors.txt: -------------------------------------------------------------------------------- 1 | authentication type unavailable for attempted privilege level 2 | RuntimeError: /usr/local/sbin/bmc-info: authentication type unavailable for attempted privilege level 3 | ipmi-fru: authentication type unavailable for attempted privilege level -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/fru.txt: -------------------------------------------------------------------------------- 1 | FRU Inventory Device: Default FRU Device (ID 00h) 2 | 3 | FRU Chassis Type: Rack Mount Chassis 4 | FRU Chassis Part Number: 430027-005 5 | FRU Chassis Serial Number: 2UX64201U2 6 | 7 | FRU Board Manufacturing Date/Time: 01/01/96 - 00:00:00 8 | FRU Board Manufacturer: HP 9 | FRU Board Product Name: ProLiant DL380 G5 10 | FRU Board Serial Number: 2UX64201U2 11 | FRU Board Part Number: 430027-005 12 | 13 | FRU Error: product info area checksum invalid 14 | -------------------------------------------------------------------------------- /spec/fixtures/freeipmi/sensors.txt: -------------------------------------------------------------------------------- 1 | 1 | System Chassis 1 UID Light | OEM Reserved | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'OEM Event = 0000h' 2 | 2 | System Chassis 2 Int. Health LED | OEM Reserved | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'OEM Event = 0000h' 3 | 3 | System Chassis 3 Ext. Health LED | OEM Reserved | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'OEM Event = 0000h' 4 | 4 | Power Supply 1 Power Supply 1 | Power Supply | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Presence detected' 5 | 5 | Power Supply 2 Power Supply 2 | Power Supply | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Presence detected' 6 | 6 | Power Supply 3 Power Supplies | Power Supply | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Fully Redundant' 7 | 8 | Processor Module 1 VRM 1 | Power Unit | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Device Inserted/Device Present' 8 | 9 | Processor Module 2 VRM 2 | Power Unit | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Device Inserted/Device Present' 9 | 10 | System Internal Expansion Board 1 Fan 1 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 10 | 11 | System Internal Expansion Board 2 Fan 2 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 11 | 12 | System Internal Expansion Board 3 Fan 3 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 12 | 13 | System Internal Expansion Board 4 Fan 4 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 13 | 14 | Processor 1 Fan 5 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 14 | 15 | Processor 2 Fan 6 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 15 | 16 | Processor 3 Fan 7 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 16 | 17 | Processor 4 Fan 8 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 17 | 18 | Processor 5 Fan 9 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 18 | 19 | Processor 6 Fan 10 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 19 | 20 | Processor 7 Fan 11 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 20 | 21 | Processor 8 Fan 12 | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'transition to Running' 21 | 22 | System Board Fans | Fan | Nominal | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Fully Redundant' 22 | 24 | System Internal Expansion Board 5 Temp 1 | Temperature | Nominal | 42.00 | C | 0.00 | 0.00 | 0.00 | 67.00 | 70.00 | 75.00 | 'OK' 23 | 25 | External Environment Temp 2 | Temperature | Nominal | 21.00 | C | 0.00 | 0.00 | 0.00 | 37.00 | 39.00 | 44.00 | 'OK' 24 | 26 | Processor 10 Temp 3 | Temperature | Nominal | 35.00 | C | 0.00 | 0.00 | 0.00 | 68.00 | 127.00 | 127.00 | 'OK' 25 | 27 | Processor 11 Temp 4 | Temperature | Nominal | 35.00 | C | 0.00 | 0.00 | 0.00 | 68.00 | 127.00 | 127.00 | 'OK' 26 | 28 | Power Unit Temp 5 | Temperature | Nominal | 49.00 | C | 0.00 | 0.00 | 0.00 | 74.00 | 77.00 | 82.00 | 'OK' 27 | 29 | Processor 12 Temp 6 | Temperature | Nominal | 35.00 | C | 0.00 | 0.00 | 0.00 | 68.00 | 127.00 | 127.00 | 'OK' 28 | 30 | Processor 13 Temp 7 | Temperature | Nominal | 35.00 | C | 0.00 | 0.00 | 0.00 | 68.00 | 127.00 | 127.00 | 'OK' 29 | 31 | System Board Power Meter | Current | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | 'Device Enabled' 30 | -------------------------------------------------------------------------------- /spec/fixtures/ipmitool/bmc_info.txt: -------------------------------------------------------------------------------- 1 | Device ID : 17 2 | Device Revision : 1 3 | Firmware Revision : 2.9 4 | IPMI Version : 2.0 5 | Manufacturer ID : 11 6 | Manufacturer Name : Hewlett-Packard 7 | Product ID : 8192 (0x2000) 8 | Product Name : Unknown (0x2000) 9 | Device Available : yes 10 | Provides Device SDRs : yes 11 | Additional Device Support : 12 | Sensor Device 13 | SDR Repository Device 14 | SEL Device 15 | FRU Inventory Device 16 | Aux Firmware Rev Info : 17 | 0x00 18 | 0x00 19 | 0x00 20 | 0x30 21 | -------------------------------------------------------------------------------- /spec/fixtures/ipmitool/errors.txt: -------------------------------------------------------------------------------- 1 | Authentication type NONE not supported 2 | Authentication type NONE not supported 3 | Error: Unable to establish LAN session 4 | Get Device ID command failed 5 | Unable to open SDR for reading 6 | 7 | Authentication type NONE not supported 8 | Authentication type NONE not supported 9 | Error: Unable to establish LAN session 10 | Get Device ID command failed -------------------------------------------------------------------------------- /spec/fixtures/ipmitool/fru.txt: -------------------------------------------------------------------------------- 1 | FRU Device Description : Builtin FRU Device (ID 0) 2 | Chassis Type : -12 V 3 | Chassis Serial : USE238F06P 4 | Board Mfg Date : Sun Dec 31 23:00:00 1995 5 | Board Mfg : HP 6 | Board Product : ProLiant SL230s Gen8 7 | Board Serial : USE238F0D0 8 | Board Part Number : 650047-B21 9 | Product Manufacturer : HP 10 | Product Name : ProLiant SL230s Gen8 11 | Product Part Number : 650047-B21 12 | Product Serial : USE238F0D0 13 | Product Asset Tag : 000015B90F82 14 | 15 | FRU Device Description : CPU 1 DIMM 1 (ID 110) 16 | Memory Size : 680 MB 17 | Memory Type : Unknown (0x0B) 18 | Voltage Intf : Unknown (0x0B) 19 | Error Detect/Cor : Unknown (0x08) 20 | Manufacturer : Unknown (0x00) 21 | Serial Number : 00000000 22 | 23 | FRU Device Description : CPU 1 DIMM 3 (ID 112) 24 | Memory Size : 680 MB 25 | Memory Type : Unknown (0x0B) 26 | Voltage Intf : Unknown (0x0B) 27 | Error Detect/Cor : Unknown (0x08) 28 | Manufacturer : Unknown (0x00) 29 | Serial Number : 00000000 30 | 31 | FRU Device Description : CPU 1 DIMM 6 (ID 115) 32 | Memory Size : 680 MB 33 | Memory Type : Unknown (0x0B) 34 | Voltage Intf : Unknown (0x0B) 35 | Error Detect/Cor : Unknown (0x08) 36 | Manufacturer : Unknown (0x00) 37 | Serial Number : 00000000 38 | 39 | FRU Device Description : CPU 1 DIMM 8 (ID 117) 40 | Memory Size : 680 MB 41 | Memory Type : Unknown (0x0B) 42 | Voltage Intf : Unknown (0x0B) 43 | Error Detect/Cor : Unknown (0x08) 44 | Manufacturer : Unknown (0x00) 45 | Serial Number : 00000000 46 | 47 | FRU Device Description : CPU 2 DIMM 1 (ID 118) 48 | Memory Size : 680 MB 49 | Memory Type : Unknown (0x0B) 50 | Voltage Intf : Unknown (0x0B) 51 | Error Detect/Cor : Unknown (0x08) 52 | Manufacturer : Unknown (0x00) 53 | Serial Number : 00000000 54 | 55 | FRU Device Description : CPU 2 DIMM 3 (ID 120) 56 | Memory Size : 680 MB 57 | Memory Type : Unknown (0x0B) 58 | Voltage Intf : Unknown (0x0B) 59 | Error Detect/Cor : Unknown (0x08) 60 | Manufacturer : Unknown (0x00) 61 | Serial Number : 00000000 62 | 63 | FRU Device Description : CPU 2 DIMM 6 (ID 123) 64 | Memory Size : 680 MB 65 | Memory Type : Unknown (0x0B) 66 | Voltage Intf : Unknown (0x0B) 67 | Error Detect/Cor : Unknown (0x08) 68 | Manufacturer : Unknown (0x00) 69 | Serial Number : 00000000 70 | 71 | FRU Device Description : CPU 2 DIMM 8 (ID 125) 72 | Memory Size : 680 MB 73 | Memory Type : Unknown (0x0B) 74 | Voltage Intf : Unknown (0x0B) 75 | Error Detect/Cor : Unknown (0x08) 76 | Manufacturer : Unknown (0x00) 77 | Serial Number : 00000000 78 | 79 | FRU Device Description : CPU 1 (ID 16) 80 | Product Manufacturer : Intel 81 | Product Name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz 82 | 83 | FRU Device Description : CPU 2 (ID 17) 84 | Product Manufacturer : Intel 85 | Product Name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz 86 | 87 | FRU Device Description : BMC CONTROLLER (ID 238) 88 | Product Manufacturer : HP 89 | Product Name : BMC CONTROLLER 90 | Product Part Number : iLO 4 91 | 92 | FRU Device Description : MB BIOS (ID 239) 93 | Product Manufacturer : HP 94 | Product Name : SYSTEM BIOS 95 | Product Part Number : P75 96 | Product Version : 07/05/2012 -------------------------------------------------------------------------------- /spec/fixtures/ipmitool/lan.txt: -------------------------------------------------------------------------------- 1 | Set in Progress : Set Complete 2 | Auth Type Support : 3 | Auth Type Enable : Callback : 4 | : User : NONE MD2 MD5 PASSWORD OEM 5 | : Operator : NONE MD2 MD5 6 | : Admin : MD2 MD5 7 | : OEM : 8 | IP Address Source : DHCP Address 9 | IP Address : 192.168.1.41 10 | Subnet Mask : 255.255.255.0 11 | MAC Address : 00:17:a4:49:ab:70 12 | BMC ARP Control : ARP Responses Enabled, Gratuitous ARP Disabled 13 | Gratituous ARP Intrvl : 0.0 seconds 14 | Default Gateway IP : 192.168.1.1 15 | 802.1q VLAN ID : Disabled 16 | 802.1q VLAN Priority : 0 17 | Cipher Suite Priv Max : Not Available 18 | -------------------------------------------------------------------------------- /spec/fixtures/ipmitool/sensors.txt: -------------------------------------------------------------------------------- 1 | FCB FAN1 | 5600.000 | RPM | ok | na | 1500.000 | na | 25500.000 | 25500.000 | 25500.000 2 | FCB FAN2 | 5600.000 | RPM | ok | na | 1500.000 | na | 25500.000 | 25500.000 | 25500.000 3 | FCB FAN3 | 5600.000 | RPM | ok | na | 1500.000 | na | 25500.000 | 25500.000 | 25500.000 4 | FCB FAN4 | 5600.000 | RPM | ok | na | 1500.000 | na | 25500.000 | 25500.000 | 25500.000 5 | PEF Action | 0x0 | discrete | 0x1080| na | na | na | na | na | na 6 | WatchDog2 | 0x0 | discrete | 0x0080| na | na | na | na | na | na 7 | AC Pwr On | 0x0 | discrete | 0x0080| na | na | na | na | na | na 8 | ACPI Pwr State | 0x0 | discrete | 0x0088| na | na | na | na | na | na 9 | FCB Ambient1 | 21.000 | degrees C | ok | na | na | na | na | 50.000 | 255.000 10 | FCB Ambient2 | 0.000 | degrees C | ok | na | na | na | na | 50.000 | 255.000 11 | CPU1Status | 0x0 | discrete | 0x0080| na | na | na | na | na | na 12 | CPU2Status | 0x0 | discrete | 0x0080| na | na | na | na | na | na 13 | PS 12V | 12.152 | Volts | ok | 10.602 | 10.850 | 11.160 | 12.834 | 13.144 | 13.392 14 | PS 5V | 5.018 | Volts | ok | 4.394 | 4.524 | 4.654 | 5.330 | 5.486 | 5.590 15 | MLB TEMP 2 | 47.000 | degrees C | ok | na | na | na | 85.000 | 87.000 | 90.000 16 | MLB TEMP 3 | 43.000 | degrees C | ok | na | na | na | 85.000 | 87.000 | 90.000 17 | Processor 1 Temp | 23.000 | degrees C | ok | na | na | na | 81.000 | 85.000 | 90.000 18 | MLB TEMP 1 | 45.000 | degrees C | ok | na | na | na | 85.000 | 87.000 | 90.000 19 | Processor 2 Temp | 24.000 | degrees C | ok | na | na | na | 81.000 | 85.000 | 90.000 20 | STBY 3.3V | 3.354 | Volts | ok | 2.907 | 2.976 | 3.079 | 3.526 | 3.629 | 3.681 21 | PS Current | 14.000 | Amps | ok | na | na | na | 255.000 | 255.000 | 255.000 22 | SEL Fullness | 0x0 | discrete | 0x0080| na | na | na | na | na | na 23 | PCI BUS | 0x0 | discrete | 0x0080| na | na | na | na | na | na 24 | Memory | 0x0 | discrete | 0x0080| na | na | na | na | na | na 25 | VCORE 1 | 1.029 | Volts | ok | na | na | na | 2.117 | 2.117 | 2.117 26 | VCORE 2 | 0.893 | Volts | ok | na | na | na | 2.958 | 2.958 | 2.958 27 | NM Capability | na | discrete | na | na | na | na | na | na | na 28 | Security | 0x0 | discrete | 0x0080| na | na | na | na | na | na 29 | PSU 1 AC Status | 0x0 | discrete | 0x0080| na | na | na | na | na | na 30 | PSU 2 AC Status | 0x0 | discrete | 0x0080| na | na | na | na | na | na 31 | PSU 1 Present | 0x0 | discrete | 0x0180| na | na | na | na | na | na 32 | PSU 2 Present | 0x0 | discrete | 0x0180| na | na | na | na | na | na 33 | PSU 2 POUT | 21.000 | Amps | ok | na | na | na | na | na | na 34 | PSU 1 POUT | 22.000 | Amps | ok | na | na | na | na | na | na 35 | UID Light | 0x0 | discrete | 0x0080| na | na | na | na | na | na 36 | Int. Health LED | 0x0 | discrete | 0x0080| na | na | na | na | na | na 37 | Ext. Health LED | 0x0 | discrete | 0x0080| na | na | na | na | na | na 38 | Power Supply 1 | 0x0 | discrete | 0x0180| na | na | na | na | na | na 39 | Power Supply 2 | 0x0 | discrete | 0x0180| na | na | na | na | na | na 40 | Power Supplies | 0x0 | discrete | 0x0180| na | na | na | na | na | na 41 | VRM 1 | 0x0 | discrete | 0x0280| na | na | na | na | na | na 42 | VRM 2 | 0x0 | discrete | 0x0280| na | na | na | na | na | na 43 | Fan 1 | 45.080 | percent | ok | na | na | na | na | na | na 44 | Fan 2 | 45.080 | percent | ok | na | na | na | na | na | na 45 | Fan 3 | 45.080 | percent | ok | na | na | na | na | na | na 46 | Fan 4 | 45.080 | percent | ok | na | na | na | na | na | na 47 | Fan 5 | 41.944 | percent | ok | na | na | na | na | na | na 48 | Fan 6 | 41.944 | percent | ok | na | na | na | na | na | na 49 | Fan 7 | 36.064 | percent | ok | na | na | na | na | na | na 50 | Fan 8 | 36.064 | percent | ok | na | na | na | na | na | na 51 | Fan 9 | 36.064 | percent | ok | na | na | na | na | na | na 52 | Fan 10 | 36.064 | percent | ok | na | na | na | na | na | na 53 | Fan 11 | 36.064 | percent | ok | na | na | na | na | na | na 54 | Fan 12 | 36.064 | percent | ok | na | na | na | na | na | na 55 | Fans | 0x0 | discrete | 0x0180| na | na | na | na | na | na 56 | Temp 1 | 37.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 67.000 | 70.000 | 75.000 57 | Temp 2 | 20.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 37.000 | 39.000 | 44.000 58 | Temp 3 | 40.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 68.000 | 127.000 | 127.000 59 | Temp 4 | 40.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 68.000 | 127.000 | 127.000 60 | Temp 5 | 49.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 74.000 | 77.000 | 82.000 61 | Temp 6 | 40.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 68.000 | 127.000 | 127.000 62 | Temp 7 | 40.000 | degrees C | ok | 0.000 | 0.000 | 0.000 | 68.000 | 127.000 | 127.000 63 | Power Meter | 374 | Watts | ok | na | na | na | na | na | na 64 | UID Light | 0x0 | discrete | 0x0080| na | na | na | na | na | na 65 | Sys. Health LED | 0x0 | discrete | 0x0080| na | na | na | na | na | na 66 | Memory | 0x0 | discrete | 0x4080| na | na | na | na | na | na 67 | Power Supply 1 | 1200.000 | Watts | nc | na | na | na | na | na | na 68 | Power Supply 2 | 1200.000 | Watts | nc | na | na | na | na | na | na 69 | Power Supply 3 | 1200.000 | Watts | nc | na | na | na | na | na | na 70 | Power Supply 4 | 1200.000 | Watts | nc | na | na | na | na | na | na 71 | Fan 3 | 46.648 | unspecified | nc | na | na | na | na | na | na 72 | Fan 4 | 46.648 | unspecified | nc | na | na | na | na | na | na 73 | 01-Inlet Ambient | 19.000 | degrees C | ok | na | na | na | na | 42.000 | 46.000 74 | 02-CPU 1 | 40.000 | degrees C | ok | na | na | na | na | 70.000 | 0.000 75 | 03-CPU 2 | 40.000 | degrees C | ok | na | na | na | na | 70.000 | 0.000 76 | 04-P1 DIMM 1-4 | 27.000 | degrees C | ok | na | na | na | na | 87.000 | 0.000 77 | 05-P1 DIMM 5-8 | 25.000 | degrees C | ok | na | na | na | na | 87.000 | 0.000 78 | 06-P2 DIMM 1-4 | 26.000 | degrees C | ok | na | na | na | na | 87.000 | 0.000 79 | 07-P2 DIMM 5-8 | 27.000 | degrees C | ok | na | na | na | na | 87.000 | 0.000 80 | 08-HD Max | 35.000 | degrees C | ok | na | na | na | na | 60.000 | 0.000 81 | 09-Chipset | 44.000 | degrees C | ok | na | na | na | na | 105.000 | 0.000 82 | 10-VR P1 | 34.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 83 | 11-VR P2 | 35.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 84 | 12-VR P1 Zone | 27.000 | degrees C | ok | na | na | na | na | 90.000 | 95.000 85 | 13-VR P2 Zone | 27.000 | degrees C | ok | na | na | na | na | 90.000 | 95.000 86 | 14-VR P1 Mem | 31.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 87 | 15-VR P1 Mem | 29.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 88 | 16-VR P2 Mem | 32.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 89 | 17-VR P2 Mem | 29.000 | degrees C | ok | na | na | na | na | 115.000 | 120.000 90 | 18-VR P1Mem Zone | 30.000 | degrees C | ok | na | na | na | na | 90.000 | 95.000 91 | 19-VR P2Mem Zone | 31.000 | degrees C | ok | na | na | na | na | 90.000 | 95.000 92 | 20-SuperCap Max | 21.000 | degrees C | ok | na | na | na | na | 65.000 | 0.000 93 | 21-HD Controller | 40.000 | degrees C | ok | na | na | na | na | 100.000 | 0.000 94 | 22-LOM | na | degrees C | na | na | na | na | na | 100.000 | 0.000 95 | 23-LOM Card | na | degrees C | na | na | na | na | na | 100.000 | 0.000 96 | 24-PCI 1 | na | degrees C | na | na | na | na | na | 100.000 | 0.000 97 | 25-PCI 2 Zone | 25.000 | degrees C | ok | na | na | na | na | 65.000 | 70.000 98 | 26-PCI 2 | 50.000 | degrees C | ok | na | na | na | na | 100.000 | 0.000 99 | 42-P/S Board | 24.000 | degrees C | ok | na | na | na | na | 80.000 | 85.000 100 | 43-Sys Exhaust | 29.000 | degrees C | ok | na | na | na | na | 90.000 | 95.000 101 | 44-HD zone | 29.000 | degrees C | ok | na | na | na | na | 65.000 | 0.000 102 | C3 P2I Bay 1 | 0.000 | unspecified | nc | na | na | na | na | na | na 103 | C3 P2I Bay 2 | 0.000 | unspecified | nc | na | na | na | na | na | na 104 | C3 P2I Bay 3 | 0.000 | unspecified | nc | na | na | na | na | na | na 105 | C3 P2I Bay 4 | 0.000 | unspecified | nc | na | na | na | na | na | na 106 | -------------------------------------------------------------------------------- /spec/integration/bmc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Bmc" do 4 | before :each do 5 | user ||= ENV["ipmiuser"] || "admin" 6 | pass ||= ENV["ipmipass"] || "password" 7 | host ||= ENV["ipmihost"] || "10.0.1.16" 8 | provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | end 11 | 12 | it "creates a bmc object" do 13 | expect(@conn.bmc).not_to be_nil 14 | end 15 | 16 | it "options should change after calling info" do 17 | before = @conn.bmc.options.clone 18 | @conn.bmc.info 19 | after = @conn.bmc.options.clone 20 | expect(before.length).to be < after.length 21 | end 22 | 23 | it 'should retrun a max retry count' do 24 | expect(@conn.bmc.max_retry_count).to be > 0 25 | end 26 | 27 | it "should reset the bmc device" do 28 | expect(@conn.bmc.reset('cold')).not_to be_nil 29 | end 30 | 31 | it "should reset the bmc device warmly" do 32 | expect(@conn.bmc.reset('warm')).not_to be_nil 33 | end 34 | 35 | it "reset should fail when type is wrong" do 36 | expect { @conn.bmc.reset('freezing') }.to raise_exception 37 | end 38 | 39 | it "is able to retrieve the bmc info" do 40 | expect(@conn.bmc.info).not_to be_nil 41 | end 42 | 43 | it "is able to retrieve the guid" do 44 | expect(@conn.bmc.guid).not_to be_nil 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/integration/chassis_config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Chassis Config" do 4 | before :each do 5 | user ||= ENV["ipmiuser"] || "admin" 6 | pass ||= ENV["ipmipass"] || "password" 7 | host ||= ENV["ipmihost"] || "10.0.1.16" 8 | provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | end 11 | 12 | it "test to set booting from PXE" do 13 | expect(@conn.chassis.config.bootpxe).to eq(true) 14 | end 15 | 16 | it "test to set booting from Disk" do 17 | expect(@conn.chassis.config.bootdisk).to eq(true) 18 | end 19 | 20 | it "test to set booting from Cdrom" do 21 | expect(@conn.chassis.config.bootcdrom).to eq(true) 22 | end 23 | 24 | it "test to set booting from bios" do 25 | expect(@conn.chassis.config.bootbios).to eq(true) 26 | end 27 | 28 | it "test to set boot persistent value" do 29 | end 30 | 31 | it "test to checkout the entire chassis config" do 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/integration/chassis_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Chassis" do 4 | before :each do 5 | user ||= ENV["ipmiuser"] || "admin" 6 | pass ||= ENV["ipmipass"] || "password" 7 | host ||= ENV["ipmihost"] || "10.0.1.16" 8 | provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | end 11 | 12 | it "test to turn uid light on for 5 seconds" do 13 | value = @conn.chassis.identify(true, 5) 14 | sleep(6) 15 | expect(value).to eq(true) 16 | end 17 | 18 | it "test to turn uid light on then off" do 19 | @conn.chassis.identify(true) 20 | sleep(2) 21 | expect(@conn.chassis.identify(false)).to eq(true) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/integration/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Connection" do 4 | before :each do 5 | user ||= ENV["ipmiuser"] || "admin" 6 | pass ||= ENV["ipmipass"] || "password" 7 | host ||= ENV["ipmihost"] || "192.168.1.16" 8 | provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | end 11 | 12 | it "creates a new object" do 13 | expect(@conn).to be_truthy 14 | end 15 | 16 | it 'creates a bmc object' do 17 | expect(@conn.bmc).to be_truthy 18 | end 19 | 20 | it 'creates a chassis object' do 21 | expect(@conn.chassis).to be_truthy 22 | end 23 | 24 | it 'can test the connection' do 25 | expect(@conn.connection_works?).to eq true 26 | end 27 | 28 | it 'can test the connection when credentials are wrong' do 29 | user ||= ENV["ipmiuser"] || "admin" 30 | pass = "wrong_password" 31 | host ||= ENV["ipmihost"] || "192.168.1.16" 32 | provider ||= ENV["ipmiprovider"] || "ipmitool" 33 | conn = Rubyipmi.connect(user, pass, host, provider) 34 | expect(conn.connection_works?).to eq false 35 | end 36 | 37 | it 'can get diag info' do 38 | expect(@conn.get_diag.keys).to eq([:provider, :frus, :sensors, :bmc_info, :version]) 39 | end 40 | 41 | it 'can get version info' do 42 | expect(@conn.bmc.version).to match(/[\d\.]{3,4}/) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/integration/fru_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Fru" do 4 | attr_accessor :provider 5 | before :each do 6 | user ||= ENV["ipmiuser"] || "admin" 7 | pass ||= ENV["ipmipass"] || "password" 8 | host ||= ENV["ipmihost"] || "10.0.1.16" 9 | provider ||= ENV["ipmiprovider"] || "ipmitool" 10 | @conn = Rubyipmi.connect(user, pass, host, provider) 11 | end 12 | 13 | it "test should return manufacturer" do 14 | expect(@conn.fru.manufacturer).not_to be nil 15 | end 16 | 17 | it "test should return serial" do 18 | expect(@conn.fru.board_serial).not_to be nil 19 | end 20 | 21 | it "test should return product name" do 22 | expect(@conn.fru.model).not_to be nil 23 | end 24 | 25 | it "test should return fru list" do 26 | expect(@conn.fru.list.length).to be >= 1 27 | end 28 | 29 | it "test missing method with known good method" do 30 | expect(@conn.fru.chassis_type).not_to be nil 31 | end 32 | 33 | it "test missing method with known bad method" do 34 | expect { @conn.fru.blah }.to raise_exception 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/integration/lan_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Lan" do 4 | before :all do 5 | @user ||= ENV["ipmiuser"] || "admin" 6 | @pass ||= ENV["ipmipass"] || "password" 7 | @host ||= ENV["ipmihost"] || "10.0.1.16" 8 | @provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | end 10 | let(:conn) { Rubyipmi.connect(@user, @pass, @host, @provider) } 11 | 12 | it "get ip address" do 13 | expect(conn.bmc.lan.ip).to eq(@host) 14 | end 15 | 16 | it "get netmask" do 17 | expect(conn.bmc.lan.netmask).to be_truthy 18 | end 19 | 20 | it "get gateway address" do 21 | expect(conn.bmc.lan.gateway).to be_truthy 22 | end 23 | 24 | it "get mac address" do 25 | expect(conn.bmc.lan.mac).to be_truthy 26 | end 27 | 28 | it "get static or dhcp" do 29 | expect(conn.bmc.lan.dhcp?).to be_truthy 30 | end 31 | 32 | it "static should be opposite of dhcp" do 33 | expect(conn.bmc.lan.dhcp?).to_not eq(conn.bmc.lan.static?) 34 | end 35 | 36 | it "should set gateway address" do 37 | gw = conn.bmc.lan.gateway 38 | conn.bmc.lan.gateway = gw 39 | expect(conn.bmc.lan.gateway = gw).to be_truthy 40 | end 41 | 42 | it "should set netmask" do 43 | netmask = conn.bmc.lan.netmask 44 | expect(conn.bmc.lan.netmask = netmask).to be_truthy 45 | end 46 | 47 | it "should set ip address" do 48 | ip = conn.bmc.lan.ip 49 | expect(conn.bmc.lan.ip = ip).to be_truthy 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/integration/power_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Power" do 4 | before :each do 5 | user ||= ENV["ipmiuser"] || "admin" 6 | pass ||= ENV["ipmipass"] || "password" 7 | host ||= ENV["ipmihost"] || "10.0.1.16" 8 | provider ||= ENV["ipmiprovider"] || "ipmitool" 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | @conn.chassis.power.off 11 | sleep(1) 12 | end 13 | 14 | it "test to turn power on" do 15 | expect(@conn.chassis.power.on).to be_truthy 16 | sleep(2) 17 | expect(@conn.chassis.power.status).to eq('on') 18 | end 19 | 20 | it "test power status" do 21 | expect(@conn.chassis.power.status).to eq('off') 22 | end 23 | 24 | it "test to check if power status is off" do 25 | @conn.chassis.power.off 26 | max_count = 12 27 | while 0 < max_count 28 | if @conn.chassis.power.off? 29 | break 30 | else 31 | sleep(5) 32 | max_count -= 1 33 | end 34 | end 35 | expect(@conn.chassis.power.off?).to be true 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/integration/rubyipmi_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'fileutils' 3 | require 'logger' 4 | 5 | describe "rubyipmi" do 6 | before :each do 7 | @user ||= ENV["ipmiuser"] || "admin" 8 | @pass ||= ENV["ipmipass"] || "password" 9 | @host ||= ENV["ipmihost"] || "10.0.1.16" 10 | @provider ||= ENV["ipmiprovider"] || "ipmitool" 11 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider) 12 | end 13 | 14 | it "creates a connection object" do 15 | conn = Rubyipmi.connect(@user, @pass, @host, @provider) 16 | expect(conn).to be_truthy 17 | end 18 | 19 | it "should test if a provider is present" do 20 | value = Rubyipmi.is_provider_installed?("ipmitool") 21 | value2 = Rubyipmi.is_provider_installed?("freeipmi") 22 | expect((value | value2)).to eq true 23 | end 24 | 25 | it "should create a connection object if freeipmi is present" do 26 | begin 27 | conn = Rubyipmi.connect(@user, @pass, @host, "freeipmi") 28 | expect(conn.kind_of?(Rubyipmi::Freeipmi::Connection)).to eq true 29 | rescue Exception => e 30 | expect(e.message.match(/freeipmi\ is\ not\ installed/)).to eq true 31 | puts "#{e.message}" 32 | end 33 | end 34 | 35 | it "should create a connection object if ipmitool is present" do 36 | begin 37 | conn = Rubyipmi.connect(@user, @pass, @host, "ipmitool") 38 | rescue Exception => e 39 | expect(e.message).to match(/ipmitool\ is\ not\ installed/) 40 | puts "#{e.message}" 41 | return true 42 | end 43 | expect(conn.kind_of?(Rubyipmi::Ipmitool::Connection)).to eq true 44 | end 45 | 46 | it "should not create a connection object if a provider is not present" do 47 | begin 48 | Rubyipmi.connect(@user, @pass, @host, "bogus") 49 | rescue Exception => e 50 | expect(e.message).to match(/The IPMI provider: bogus is not installed/) 51 | end 52 | end 53 | 54 | it "check to find any available installed providers" do 55 | expect(Rubyipmi.providers_installed.length).to be > 0 56 | end 57 | 58 | it 'can get diag info' do 59 | # must have both freeipmi and ipmitool for this to pass 60 | Rubyipmi.get_diag(@user, @pass, @host) 61 | expect(File.exist?('/tmp/rubyipmi_diag_data.txt')).to be true 62 | FileUtils.rm('/tmp/rubyipmi_diag_data.txt') 63 | end 64 | 65 | describe :logger do 66 | before :each do 67 | FileUtils.rm_f('/tmp/rubyipmi.log') 68 | Rubyipmi.log_level = nil 69 | Rubyipmi.logger = nil 70 | end 71 | 72 | it 'should only create an info log level' do 73 | Rubyipmi.log_level = Logger::INFO 74 | Rubyipmi.get_diag(@user, @pass, @host) 75 | expect(File.exist?('/tmp/rubyipmi.log')).to be true 76 | size = File.open('/tmp/rubyipmi.log', 'r') { |f| f.read.length } 77 | expect(size).to be_within(60).of(100) 78 | end 79 | 80 | it 'should create a log with debug level' do 81 | Rubyipmi.log_level = Logger::DEBUG 82 | Rubyipmi.get_diag(@user, @pass, @host) 83 | expect(File.exist?('/tmp/rubyipmi.log')).to be true 84 | size = File.open('/tmp/rubyipmi.log', 'r') { |f| f.read.length } 85 | expect(size).to be > 100 86 | end 87 | 88 | it 'should use custom logger' do 89 | FileUtils.rm_f('/tmp/rubyipmi_custom.log') 90 | logger = Logger.new('/tmp/rubyipmi_custom.log') 91 | logger.level = Logger::DEBUG 92 | Rubyipmi.logger = logger 93 | Rubyipmi.get_diag(@user, @pass, @host) 94 | expect(File.exist?('/tmp/rubyipmi_custom.log')).to be true 95 | size = File.open('/tmp/rubyipmi_custom.log', 'r') { |f| f.read.length } 96 | expect(size).to be > 100 97 | FileUtils.rm_f('/tmp/rubyipmi_custom.log') 98 | end 99 | 100 | it 'should not create a log file when log level is nil' do 101 | Rubyipmi.get_diag(@user, @pass, @host) 102 | expect(Rubyipmi.logger.instance_of?(NullLogger)).to be true 103 | expect(File.exist?('/tmp/rubyipmi.log')).to be false 104 | end 105 | 106 | it 'should not create a log file when logger is set to nil and log_level is nil' do 107 | Rubyipmi.get_diag(@user, @pass, @host) 108 | expect(Rubyipmi.logger.instance_of?(NullLogger)).to be true 109 | expect(File.exist?('/tmp/rubyipmi.log')).to be false 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/integration/sensor_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | describe "Sensors" do 3 | attr_accessor :provider 4 | before :each do 5 | user = ENV["ipmiuser"] || 'admin' 6 | pass = ENV["ipmipass"] || 'password' 7 | host = ENV["ipmihost"] || "10.0.1.16" 8 | provider = ENV["ipmiprovider"] || 'ipmitool' 9 | @conn = Rubyipmi.connect(user, pass, host, provider) 10 | end 11 | 12 | it "test get all sensors" do 13 | expect(@conn.sensors.list.count).to be > 1 14 | end 15 | 16 | it "test should refresh data" do 17 | old = @conn.sensors.list 18 | @conn.sensors.refresh 19 | new = @conn.sensors.list 20 | expect(old).not_to equal(new) 21 | end 22 | 23 | it "test should return count greater than 1" do 24 | expect(@conn.sensors.count).to be > 1 25 | end 26 | 27 | it "test should return names" do 28 | expect(@conn.sensors.names.count).to be > 1 29 | end 30 | 31 | it "test should return list of fans" do 32 | expect(@conn.sensors.fanlist.count).to be > 1 33 | end 34 | 35 | it "test should return list of temps" do 36 | expect(@conn.sensors.templist.count).to be > 1 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/manifests/default.pp: -------------------------------------------------------------------------------- 1 | $packages_installed = ['libgcrypt-devel', 'ipmitool', 'gcc', 'make', 'ruby-devel'] 2 | $freeipmi_version = '1.3.2' 3 | $freeipmi_name = "freeipmi-${freeipmi_version}" 4 | $freeipmi_dir = "/opt/${freeipmi_name}" 5 | $directories_created = ['/opt'] 6 | $gems_installed = ['bundler'] 7 | $rubyipmi_dir = '/rubyipmi' 8 | archive { "freeipmi-${freeipmi_version}": 9 | ensure => present, 10 | url => "http://ftp.gnu.org/gnu/freeipmi/freeipmi-${freeipmi_version}.tar.gz", 11 | target => '/opt', 12 | checksum => false, 13 | before => Exec["configure_freeipmi"], 14 | require => File['/opt'], 15 | src_target => '/opt' 16 | } 17 | Exec{ 18 | path => ['/usr/bin/', '/sbin', '/bin', '/usr/local/bin', '/usr/local/sbin', '/usr/sbin'] 19 | } 20 | package{$gems_installed: 21 | ensure => present, 22 | provider => 'gem', 23 | before => Exec["configure_freeipmi"] 24 | } 25 | package{$packages_installed: 26 | ensure => present, 27 | before => Exec["configure_freeipmi"] 28 | } 29 | file{$directories_created: 30 | ensure => directory, 31 | before => Exec['configure_freeipmi'] 32 | } 33 | exec{"configure_freeipmi": 34 | cwd => $freeipmi_dir, 35 | command => "bash ${freeipmi_dir}/configure", 36 | creates => "${freeipmi_dir}/ipmipower/ipmipower", 37 | before => Exec["install_freeipmi"] 38 | } 39 | exec{"install_freeipmi": 40 | cwd => $freeipmi_dir, 41 | command => "make install", 42 | creates => "/usr/local/sbin/ipmipower" 43 | } 44 | 45 | exec{"bundle install": 46 | cwd => $rubyipmi_dir, 47 | creates => "${rubyipmi_dir}/Gem.lock" 48 | 49 | 50 | } -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | require 'rspec' 4 | require 'rubyipmi' 5 | require 'coveralls' 6 | Coveralls.wear! 7 | # Requires supporting files with custom matchers and macros, etc, 8 | # in ./support/ and its subdirectories. 9 | 10 | # Dir["#{File.dirname(__FILE__)}/unit/**/*.rb"].each {|f| require f} 11 | 12 | def command_is_eql?(source, expected) 13 | src = source.split(' ') 14 | exp = expected.split(' ') 15 | exp - src 16 | end 17 | 18 | def verify_freeipmi_command(cmdobj, exp_args_count, expcmd) 19 | actual = cmdobj.lastcall 20 | actual.scan(/(^#{Regexp.escape(expcmd)})/) do |cmd_match| 21 | expect(cmd_match.first).to eq(expcmd) 22 | end 23 | args_match = actual.scan(/(\-{2}[\w-]*=?[-\w\/]*)/) 24 | # not sure how to exactly test for arguments since they could vary, so we will need to use count for now 25 | # args_match.should =~ exp_args 26 | expect(args_match.count).to eq(exp_args_count) 27 | end 28 | 29 | def verify_ipmitool_command(cmdobj, exp_args_count, expcmd, required_args) 30 | actual = cmdobj.lastcall 31 | actual.scan(/(^#{Regexp.escape(expcmd)})/) do |cmd_match| 32 | expect(cmd_match.first).to eq(expcmd) 33 | end 34 | args_match = actual.scan(/(-\w\s[\w\d\S]*)/) 35 | expect(actual.include?(required_args)).to eq true 36 | # not sure how to exactly test for arguments since they could vary, so we will need to use count for now 37 | # args_match.should =~ exp_args 38 | expect(args_match.count).to eq(exp_args_count) 39 | end 40 | 41 | RSpec.configure do |config| 42 | config.expect_with :rspec do |c| 43 | c.syntax = [:expect] 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/bmc-info_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Bmc" do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | provider = "freeipmi" 11 | user = "ipmiuser" 12 | pass = "impipass" 13 | host = "ipmihost" 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return("#{@path}/ipmipower") 15 | 16 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 17 | @bmcinfo = @conn.bmc.information 18 | data = nil 19 | File.open("spec/fixtures/#{provider}/bmc_info.txt", 'r') do |file| 20 | data = file.read 21 | end 22 | allow(@bmcinfo).to receive(:locate_command).with('bmc-info').and_return("#{@path}/bmc-info") 23 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 24 | end 25 | 26 | it "cmd should be bmc-info with correct number of arguments" do 27 | @bmcinfo.retrieve 28 | verify_freeipmi_command(@bmcinfo, 3, "#{@path}/bmc-info") 29 | end 30 | 31 | it "cmd should be bmc-info with correct number of arguments" do 32 | @bmcinfo.guid 33 | verify_freeipmi_command(@bmcinfo, 4, "#{@path}/bmc-info") 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/bmc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Bmc" do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | provider = "freeipmi" 11 | user = "ipmiuser" 12 | pass = "impipass" 13 | host = "ipmihost" 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return("#{@path}/ipmipower") 15 | 16 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 17 | @bmc = @conn.bmc 18 | data = nil 19 | File.open("spec/fixtures/#{provider}/bmc_info.txt", 'r') do |file| 20 | data = file.read 21 | end 22 | allow(@bmc).to receive(:guid).and_return("guid") 23 | allow(@bmc).to receive(:info).and_return("info") 24 | end 25 | 26 | it "bmc should not be nil" do 27 | expect(@bmc).not_to be nil 28 | end 29 | 30 | it "lan should not be nil" do 31 | expect(@bmc.lan).not_to be_nil 32 | end 33 | 34 | it "guid should not be nil" do 35 | expect(@bmc.guid).not_to be_nil 36 | end 37 | 38 | it "info should not be nill" do 39 | expect(@bmc.info).not_to be_nil 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Bmc" do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | @provider = "freeipmi" 7 | @user = "ipmiuser" 8 | @pass = "impipass" 9 | @host = "ipmihost" 10 | end 11 | 12 | before :each do 13 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return("#{@path}/ipmipower") 14 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true) 15 | end 16 | 17 | it "connection should not be nil" do 18 | expect(@conn).not_to be_nil 19 | end 20 | 21 | it "fru should not be nil" do 22 | expect(@conn.fru).not_to be_nil 23 | end 24 | 25 | it "provider should not be nil" do 26 | expect(@conn.provider).not_to be_nil 27 | end 28 | 29 | it "provider should be freeipmi" do 30 | expect(@conn.provider).to eq("freeipmi") 31 | end 32 | 33 | it "bmc should not be nil" do 34 | expect(@conn.bmc).not_to be_nil 35 | end 36 | 37 | it "sensors should not be nil" do 38 | expect(@conn.sensors).not_to be_nil 39 | end 40 | 41 | it "chassis should not be nill" do 42 | expect(@conn.chassis).not_to be_nil 43 | end 44 | 45 | it 'object should have driver set to lan20 if not specified' do 46 | expect(@conn.options['driver-type']).to eq "LAN_2_0" 47 | end 48 | 49 | it 'object should have driver set to auto if not specified' do 50 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 51 | expect(@conn.options.key?('driver-type')).to eq false 52 | end 53 | 54 | it 'object should have priv type set to ADMINISTRATOR if not specified' do 55 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 56 | expect(@conn.options.key?('privilege-level')).to eq false 57 | end 58 | 59 | it 'object should have priv type set to USER ' do 60 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :privilege => 'USER', :debug => true, :driver => 'auto') 61 | expect(@conn.options.fetch('privilege-level')).to eq('USER') 62 | end 63 | 64 | it 'should raise exception if invalid privilege type' do 65 | expect { Rubyipmi.connect(@user, @pass, @host, @provider, :privilege => 'BLAH', :debug => true, :driver => 'auto') }.to raise_error(RuntimeError) 66 | end 67 | 68 | it 'should raise exception if invalid driver type' do 69 | expect { Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'foo') }.to raise_error(RuntimeError) 70 | end 71 | 72 | it 'object should have driver set to lan_2_0' do 73 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'lan20') 74 | expect(@conn.options['driver-type']).to eq('LAN_2_0') 75 | end 76 | 77 | it 'object should have driver set to lan' do 78 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'lan15') 79 | expect(@conn.options['driver-type']).to eq('LAN') 80 | end 81 | 82 | it 'object should have driver set to openipmi' do 83 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'open') 84 | expect(@conn.options['driver-type']).to eq('OPENIPMI') 85 | end 86 | 87 | describe 'test' do 88 | it 'should retrun boolean on test connection when result is not a hash' do 89 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 90 | bmc = double 91 | allow(bmc).to receive(:info).and_return('') 92 | allow(conn).to receive(:bmc).and_return(bmc) 93 | expect(conn.connection_works?).to eq false 94 | end 95 | 96 | it 'should retrun boolean on test connection when result is a hash' do 97 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 98 | bmc = double 99 | allow(bmc).to receive(:info).and_return(:test => true) 100 | allow(conn).to receive(:bmc).and_return(bmc) 101 | expect(conn.connection_works?).to eq true 102 | end 103 | 104 | it 'should retrun boolean on test connection when nil' do 105 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 106 | bmc = double 107 | allow(bmc).to receive(:info).and_return(nil) 108 | allow(conn).to receive(:bmc).and_return(bmc) 109 | expect(conn.connection_works?).to eq false 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/errorcodes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | # require 'rubyipmi/Freeipmi/errorcodes' 3 | 4 | describe "Errorcodes" do 5 | it 'should return the length of fix hash' do 6 | expect(Rubyipmi::Freeipmi::ErrorCodes.length).to be >= 1 7 | end 8 | 9 | it 'should return a hash of codes' do 10 | expect(Rubyipmi::Freeipmi::ErrorCodes.code).to be_an_instance_of Hash 11 | end 12 | 13 | it 'should return a fix if code is found' do 14 | code = 'authentication type unavailable for attempted privilege level' 15 | expect(Rubyipmi::Freeipmi::ErrorCodes.search(code)).to eq("driver-type" => "LAN_2_0") 16 | end 17 | 18 | it 'should throw and error if no fix is found' do 19 | code = 'Crap Shoot' 20 | expect { Rubyipmi::Freeipmi::ErrorCodes.search(code) }.to raise_error 21 | end 22 | 23 | it 'should throw and error when a bad code is given' do 24 | code = nil 25 | expect { Rubyipmi::Freeipmi::ErrorCodes.search(code) }.to raise_error 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/fru_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | describe :Fru do 3 | before :all do 4 | @path = '/usr/local/bin' 5 | end 6 | 7 | before :each do 8 | allow_message_expectations_on_nil 9 | data = nil 10 | provider = "freeipmi" 11 | user = "ipmiuser" 12 | pass = "impipass" 13 | host = "ipmihost" 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return("#{@path}/ipmipower") 15 | 16 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 17 | @fru = @conn.fru 18 | File.open("spec/fixtures/#{provider}/fru.txt", 'r') do |file| 19 | data = file.read 20 | end 21 | 22 | allow(@fru).to receive(:locate_command).with('ipmi-fru').and_return("#{@path}/ipmi-fru") 23 | allow(@fru).to receive(:`).and_return(data) 24 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 25 | end 26 | 27 | it "cmd should be ipmi-fru with correct number of arguments" do 28 | @fru.list 29 | verify_freeipmi_command(@fru, 3, "#{@path}/ipmi-fru") 30 | end 31 | 32 | it 'should list data' do 33 | expect(@fru.names.count).to eq(1) 34 | end 35 | 36 | it 'should return a list of unparsed frus' do 37 | expect(@fru.getfrus).not_to be_nil 38 | end 39 | 40 | it "should return a list of parsed frus" do 41 | expect(@fru.list.count).to eq(1) 42 | end 43 | 44 | it 'should return a manufacturer' do 45 | expect(@fru.board_manufacturer).to eq('HP') 46 | end 47 | 48 | it 'should return a product' do 49 | expect(@fru.board_product_name).to eq('ProLiant DL380 G5') 50 | end 51 | 52 | it 'should return a chassis serial' do 53 | expect(@fru.chassis_serial_number).to eq('2UX64201U2') 54 | end 55 | 56 | it 'should return a board serial' do 57 | expect(@fru.board_serial_number).to eq('2UX64201U2') 58 | end 59 | 60 | it 'should return a list of fru names' do 61 | expect(@fru.names.count).to eq(1) 62 | end 63 | 64 | it 'should return a fru using method missing' do 65 | @fru.names.each do |name| 66 | fru = @fru.send(name) 67 | expect(fru).to be_an_instance_of(Rubyipmi::Freeipmi::FruData) 68 | expect(fru[:name]).to eq(name) 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/unit/freeipmi/lan_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logicminds/rubyipmi/405c5eff6c750971650495cb8aef61eed214eda4/spec/unit/freeipmi/lan_spec.rb -------------------------------------------------------------------------------- /spec/unit/freeipmi/sensors_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe :Sensors do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | data = nil 11 | provider = "freeipmi" 12 | user = "ipmiuser" 13 | pass = "impipass" 14 | host = "ipmihost" 15 | # this stub allows us to mock the command that would be used to verify provider installation 16 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return("#{@path}/ipmipower") 17 | 18 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 19 | @sensors = @conn.sensors 20 | File.open("spec/fixtures/#{provider}/sensors.txt", 'r') do |file| 21 | data = file.read 22 | end 23 | # this stub allows us to mock the command that is used with this test case 24 | allow(@sensors).to receive(:locate_command).with('ipmi-sensors').and_return('/usr/local/bin/ipmi-sensors') 25 | 26 | # these stubs allow us to run the command and return the fixtures 27 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 28 | end 29 | 30 | it "cmd should be ipmi-sensors with six arguments" do 31 | @sensors.list 32 | verify_freeipmi_command(@sensors, 6, "#{@path}/ipmi-sensors") 33 | end 34 | 35 | it "can return a list of sensors" do 36 | expect(@sensors.list).not_to be_nil 37 | end 38 | 39 | it "should return a count of sensors" do 40 | expect(@sensors.count).to eq(29) 41 | end 42 | 43 | it "should return a list of fan names" do 44 | expect(@sensors.fanlist.count).to eq(13) 45 | end 46 | 47 | it 'should return a list of temp names' do 48 | expect(@sensors.templist.count).to eq(7) 49 | end 50 | 51 | it 'should return a list of sensor names as an array' do 52 | expect(@sensors.names).to be_an_instance_of(Array) 53 | expect(@sensors.names.count).to eq(29) 54 | end 55 | 56 | it 'should return an empty list if no data exists' do 57 | allow(@sensors).to receive(:getsensors).and_return(nil) 58 | expect(@sensors.names.count).to eq(0) 59 | end 60 | 61 | it 'should return a sensor using method missing' do 62 | @sensors.names.each do |name| 63 | sensor = @sensors.send(name) 64 | expect(sensor).to be_an_instance_of(Rubyipmi::Freeipmi::Sensor) 65 | expect(sensor[:name]).to eq(name) 66 | end 67 | end 68 | 69 | # it 'fix should be added to options after error occurs' do 70 | # error = nil 71 | # File.open("spec/fixtures/freeipmi/errors.txt",'r') do |file| 72 | # error = file.read 73 | # end 74 | # @sensors.stub(:`).and_return(error) 75 | # $?.stub(:success?).and_return(false) 76 | # @sensors.refresh 77 | # after = @sensors.options.fetch('driver-type', false).should_not be_false 78 | # end 79 | end 80 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/bmc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Bmc" do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | provider = "ipmitool" 11 | user = "ipmiuser" 12 | pass = "impipass" 13 | host = "ipmihost" 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 15 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 16 | @bmc = @conn.bmc 17 | data = nil 18 | File.open("spec/fixtures/#{provider}/bmc_info.txt", 'r') do |file| 19 | data = file.read 20 | end 21 | 22 | allow(@bmc).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 23 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 24 | 25 | allow(@bmc).to receive(:guid).and_return("guid") 26 | end 27 | 28 | it "bmc should not be nil" do 29 | expect(@bmc).not_to be nil 30 | end 31 | 32 | it "lan should not be nil" do 33 | expect(@bmc.lan).not_to be_nil 34 | end 35 | 36 | it "guid should not be nil" do 37 | expect(@bmc.guid).not_to be_nil 38 | end 39 | 40 | it "info should not be nil" do 41 | expect(@bmc.info).not_to be_nil 42 | end 43 | 44 | it "info should parse as expected" do 45 | expect(@bmc.info).to eq(RETRIEVE) 46 | end 47 | end 48 | 49 | RETRIEVE = { 50 | 'Device ID' => '17', 51 | 'Device Revision' => '1', 52 | 'Firmware Revision' => '2.9', 53 | 'IPMI Version' => '2.0', 54 | 'Manufacturer ID' => '11', 55 | 'Manufacturer Name' => 'Hewlett-Packard', 56 | 'Product ID' => '8192 (0x2000)', 57 | 'Product Name' => 'Unknown (0x2000)', 58 | 'Device Available' => 'yes', 59 | 'Provides Device SDRs' => 'yes', 60 | 'Additional Device Support' => [ 61 | 'Sensor Device', 62 | 'SDR Repository Device', 63 | 'SEL Device', 64 | 'FRU Inventory Device' 65 | ], 66 | 'Aux Firmware Rev Info' => [ 67 | '0x00', 68 | '0x00', 69 | '0x00', 70 | '0x30' 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe :Connection do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | @provider = "ipmitool" 7 | @user = "ipmiuser" 8 | @pass = "impipass" 9 | @host = "ipmihost" 10 | end 11 | 12 | before :each do 13 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 14 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true) 15 | end 16 | 17 | it "connection should not be nil" do 18 | expect(@conn).not_to be_nil 19 | end 20 | 21 | it "fru should not be nil" do 22 | expect(@conn.fru).not_to be_nil 23 | end 24 | 25 | it "provider should not be nil" do 26 | expect(@conn.provider).not_to be_nil 27 | end 28 | 29 | it "provider should be ipmitool" do 30 | expect(@conn.provider).to eq("ipmitool") 31 | end 32 | 33 | it "bmc should not be nil" do 34 | expect(@conn.bmc).not_to be_nil 35 | end 36 | 37 | it "sensors should not be nil" do 38 | expect(@conn.sensors).not_to be_nil 39 | end 40 | 41 | it "chassis should not be nill" do 42 | expect(@conn.chassis).not_to be_nil 43 | end 44 | 45 | it "provider should return ipmitool" do 46 | expect(@conn.provider).to eq("ipmitool") 47 | end 48 | 49 | it 'object should have driver set to auto if not specified' do 50 | expect(@conn.options['I']).to eq 'lanplus' 51 | end 52 | 53 | it 'object should have driver set to auto if not specified' do 54 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 55 | expect(@conn.options.key?('I')).to eq false 56 | end 57 | 58 | it 'should raise exception if invalid driver type' do 59 | expect { Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'foo') }.to raise_error(RuntimeError) 60 | end 61 | 62 | it 'object should have priv type set to ADMINISTRATOR if not specified' do 63 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 64 | expect(@conn.options.key?('L')).to eq false 65 | end 66 | 67 | it 'object should have priv type set to USER ' do 68 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :privilege => 'USER', :debug => true, :driver => 'auto') 69 | expect(@conn.options.fetch('L')).to eq('USER') 70 | end 71 | 72 | it 'should raise exception if invalid privilege type' do 73 | expect { Rubyipmi.connect(@user, @pass, @host, @provider, :privilege => 'BLAH', :debug => true, :driver => 'auto') }.to raise_error(RuntimeError) 74 | end 75 | 76 | it 'object should have driver set to lanplus' do 77 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'lan20') 78 | expect(@conn.options['I']).to eq('lanplus') 79 | end 80 | 81 | it 'object should have driver set to lanplus' do 82 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'lan15') 83 | expect(@conn.options['I']).to eq('lan') 84 | end 85 | 86 | it 'object should have driver set to open' do 87 | @conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'open') 88 | expect(@conn.options['I']).to eq('open') 89 | end 90 | 91 | describe 'test' do 92 | it 'should retrun boolean on test connection when result is not a hash' do 93 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 94 | bmc = double 95 | allow(bmc).to receive(:info).and_return('') 96 | allow(conn).to receive(:bmc).and_return(bmc) 97 | expect(conn.connection_works?).to eq false 98 | end 99 | 100 | it 'should retrun boolean on test connection when result is a hash' do 101 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 102 | bmc = double 103 | allow(bmc).to receive(:info).and_return(:test => true) 104 | allow(conn).to receive(:bmc).and_return(bmc) 105 | expect(conn.connection_works?).to eq true 106 | end 107 | 108 | it 'should retrun boolean on test connection when nil' do 109 | conn = Rubyipmi.connect(@user, @pass, @host, @provider, :debug => true, :driver => 'auto') 110 | bmc = double 111 | allow(bmc).to receive(:info).and_return(nil) 112 | allow(conn).to receive(:bmc).and_return(bmc) 113 | expect(conn.connection_works?).to eq false 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/errorcodes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'rubyipmi/ipmitool/errorcodes' 3 | 4 | describe "Errorcodes" do 5 | it 'should return the length of fix hash' do 6 | expect(Rubyipmi::Ipmitool::ErrorCodes.length).to be > 1 7 | end 8 | 9 | it 'should return a hash of codes' do 10 | expect(Rubyipmi::Ipmitool::ErrorCodes.code).to be_an_instance_of Hash 11 | end 12 | 13 | it 'should return a fix if code is found' do 14 | code = 'Authentication type NONE not supported' 15 | expect(Rubyipmi::Ipmitool::ErrorCodes.search(code)).to eq("I" => "lanplus") 16 | end 17 | 18 | it 'should throw and error if no fix is found' do 19 | code = 'Crap Shoot' 20 | expect { Rubyipmi::Ipmitool::ErrorCodes.search(code) }.to raise_error 21 | end 22 | 23 | it 'should throw and error when a bad code is given' do 24 | code = nil 25 | expect { Rubyipmi::Ipmitool::ErrorCodes.search(code) }.to raise_error 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/fru_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe :Fru do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | data = nil 11 | provider = "ipmitool" 12 | user = "ipmiuser" 13 | pass = "impipass" 14 | host = "ipmihost" 15 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 16 | 17 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 18 | @fru = @conn.fru 19 | 20 | File.open("spec/fixtures/#{provider}/fru.txt", 'r') do |file| 21 | data = file.read 22 | end 23 | 24 | allow(@fru).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 25 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 26 | end 27 | 28 | it "cmd should be ipmi-sensors with correct number of arguments" do 29 | @fru.list 30 | verify_ipmitool_command(@fru, 4, "#{@path}/ipmitool", 'fru') 31 | end 32 | 33 | it 'should return a list of unparsed frus' do 34 | expect(@fru.getfrus).not_to be_nil 35 | end 36 | 37 | it 'should return a list of fru names' do 38 | expect(@fru.names.count).to eq(13) 39 | end 40 | 41 | it "should return a list of parsed frus" do 42 | expect(@fru.list.count).to eq(13) 43 | end 44 | 45 | it 'should return a manufactor' do 46 | expect(@fru.product_manufacturer).to eq('HP') 47 | end 48 | 49 | it 'should return a product' do 50 | expect(@fru.product_name).to eq('ProLiant SL230s Gen8') 51 | end 52 | 53 | it 'should return a board serial' do 54 | expect(@fru.board_serial).to eq('USE238F0D0') 55 | end 56 | 57 | it 'should return a product serial' do 58 | expect(@fru.product_serial).to eq('USE238F0D0') 59 | end 60 | 61 | it 'should return a asset tag' do 62 | expect(@fru.product_asset_tag).to eq('000015B90F82') 63 | end 64 | 65 | it 'should return a fru using method missing' do 66 | @fru.names.each do |name| 67 | fru = @fru.send(name) 68 | expect(fru).to be_an_instance_of(Rubyipmi::Ipmitool::FruData) 69 | expect(fru[:name]).to eq(name) 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/lan_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Lan" do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | provider = "ipmitool" 11 | user = "ipmiuser" 12 | pass = "impipass" 13 | host = "ipmihost" 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 15 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 16 | @lan = @conn.bmc.lan 17 | data = nil 18 | File.open("spec/fixtures/#{provider}/lan.txt", 'r') do |file| 19 | data = file.read 20 | end 21 | 22 | allow(@lan).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 23 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 24 | end 25 | 26 | it "cmd should be lan with correct number of arguments" do 27 | @lan.info 28 | verify_ipmitool_command(@lan, 4, "#{@path}/ipmitool", 'lanplus') 29 | end 30 | 31 | it "can return a lan information" do 32 | expect(@lan.info).not_to be_nil 33 | end 34 | 35 | it "can print valid lan info" do 36 | expect(@lan.info.length).to be > 1 37 | end 38 | 39 | it 'should print valid ip address' do 40 | expect(@lan.ip).to eq('192.168.1.41') 41 | end 42 | 43 | it 'should print valid snmp string' do 44 | expect(@lan.snmp).to be_nil 45 | end 46 | 47 | it 'should print correct mac address' do 48 | expect(@lan.mac).to eq('00:17:a4:49:ab:70') 49 | end 50 | 51 | it 'should print correct netmask' do 52 | expect(@lan.netmask).to eq('255.255.255.0') 53 | end 54 | 55 | it 'should print correct gateway' do 56 | expect(@lan.gateway).to eq('192.168.1.1') 57 | end 58 | 59 | it 'should print vlanid' do 60 | expect(@lan.vlanid).to be_nil 61 | end 62 | 63 | it 'dhcp should return true' do 64 | expect(@lan.dhcp?).to eq true 65 | end 66 | 67 | it 'static should return false' do 68 | expect(@lan.static?).to eq false 69 | end 70 | 71 | # it 'should attempt to apply fix and fail, then switch to channel 1' do 72 | # channelbefore = @lan.channel 73 | # error = "some lan channel problem" 74 | # @lan.stub(:`).and_return(error) 75 | # $?.stub(:success?).and_return(false) 76 | # @lan.ip 77 | # channelbefore = @lan.channel 78 | # 79 | # data = nil 80 | # File.open("spec/fixtures/#{provider}/lan.txt",'r') do |file| 81 | # data = file.read 82 | # end 83 | # 84 | # @lan.stub(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 85 | # @lan.stub(:`).and_return(data) 86 | # $?.stub(:success?).and_return(true) 87 | # 88 | # end 89 | end 90 | -------------------------------------------------------------------------------- /spec/unit/ipmitool/sensors_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe :Sensors do 4 | before :all do 5 | @path = '/usr/local/bin' 6 | end 7 | 8 | before :each do 9 | allow_message_expectations_on_nil 10 | 11 | data = nil 12 | provider = "ipmitool" 13 | user = "ipmiuser" 14 | pass = "impipass" 15 | host = "ipmihost" 16 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 17 | 18 | @conn = Rubyipmi.connect(user, pass, host, provider, :debug => true) 19 | @sensors = @conn.sensors 20 | File.open("spec/fixtures/#{provider}/sensors.txt", 'r') do |file| 21 | data = file.read 22 | end 23 | allow(Rubyipmi).to receive(:capture3).and_return([data, '', true]) 24 | end 25 | 26 | # it 'should figure out to add the -I lanplus' do 27 | # error = 'Authentication type NONE not supported' 28 | # @sensors.stub(:`).and_return(error) 29 | # @sensors.list 30 | # @sensors.lastcall.includes?('-I lanplus') 31 | # end 32 | 33 | # it "cmd should be ipmi-sensors with three arguments" do 34 | # @sensors.list 35 | # verify_ipmitool_command(@sensors, 3, "#{@path}/ipmitool", 'sensor') 36 | # end 37 | 38 | it "can return a list of sensors" do 39 | expect(@sensors.list).not_to be_nil 40 | end 41 | 42 | it "should return a count of sensors" do 43 | expect(@sensors.count).to eq(99) 44 | end 45 | 46 | it "should return a list of fan names" do 47 | expect(@sensors.fanlist.count).to eq(17) 48 | end 49 | 50 | it 'should return a list of temp names' do 51 | expect(@sensors.templist.count).to eq(43) 52 | @sensors.templist.each do |_temp| 53 | end 54 | end 55 | 56 | it 'should return a list of sensor names as an array' do 57 | expect(@sensors.names).to be_an_instance_of(Array) 58 | expect(@sensors.names.count).to eq(99) 59 | end 60 | 61 | it 'should return an empty list if no data exists' do 62 | allow(@sensors).to receive(:getsensors).and_return(nil) 63 | expect(@sensors.names.count).to eq(0) 64 | end 65 | 66 | it 'should return a sensor using method missing' do 67 | @sensors.names.each do |name| 68 | sensor = @sensors.send(name) 69 | expect(sensor).to be_an_instance_of(Rubyipmi::Ipmitool::Sensor) 70 | end 71 | end 72 | 73 | it "test should create new Sensor" do 74 | expect(Rubyipmi::Ipmitool::Sensor.new("fakesensor")).not_to be nil 75 | end 76 | 77 | # it 'fix should be added to options after error occurs' do 78 | # error = nil 79 | # File.open("spec/fixtures/ipmitool/errors.txt",'r') do |file| 80 | # error = file.read 81 | # end 82 | # @sensors.stub(:`).and_return(error) 83 | # $?.stub(:success?).and_return(false) 84 | # @sensors.list 85 | # after = @sensors.options.fetch('I', false).should_not be_false 86 | # end 87 | end 88 | -------------------------------------------------------------------------------- /spec/unit/rubyipmi_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') 2 | 3 | describe :Rubyipmi do 4 | before :each do 5 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return("#{@path}/ipmitool") 6 | end 7 | 8 | it 'is provider installed should return ipmitool true' do 9 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return('/usr/local/bin/ipmitool') 10 | expect(Rubyipmi.is_provider_installed?('ipmitool')).to eq true 11 | end 12 | 13 | it 'is locate command should return command in /usr/local/bin' do 14 | allow(Rubyipmi).to receive(:locate_command).with('ipmitool').and_return('/usr/local/bin/ipmitool') 15 | expect(Rubyipmi.locate_command('ipmitool')).to eq('/usr/local/bin/ipmitool') 16 | end 17 | 18 | it 'is provider installed should return freeipmi true' do 19 | allow(Rubyipmi).to receive(:locate_command).with('ipmipower').and_return('/usr/local/bin/ipmipower') 20 | expect(Rubyipmi.is_provider_installed?('freeipmi')).to eq true 21 | end 22 | 23 | it 'is provider installed should return false when bad provider' do 24 | expect { Rubyipmi.is_provider_installed?('bad_provider') }.to_not raise_error 25 | expect(Rubyipmi.is_provider_installed?('bad_provider')).to be_falsey 26 | end 27 | 28 | it 'converts string to symbols' do 29 | user = "ipmiuser" 30 | pass = "impipass" 31 | host = "ipmihost" 32 | provider = "ipmitool" 33 | conn = Rubyipmi.connect(user, pass, host, provider, 'driver' => 'lan15') 34 | expect(conn.options).to eq("H" => "ipmihost", "U" => "ipmiuser", "P" => "impipass", "I" => "lan") 35 | end 36 | 37 | it 'does not error when converting strings to symbols' do 38 | user = "ipmiuser" 39 | pass = "impipass" 40 | host = "ipmihost" 41 | provider = "ipmitool" 42 | conn = Rubyipmi.connect(user, pass, host, provider, :driver => 'lan15') 43 | expect(conn.options).to eq("H" => "ipmihost", "U" => "ipmiuser", "P" => "impipass", "I" => "lan") 44 | end 45 | end 46 | --------------------------------------------------------------------------------