pax_global_header00006660000000000000000000000064143667763370014540gustar00rootroot0000000000000052 comment=ba1d466a2a6126a680b0b4b197d595dc37438a5f python-ipmi-0.5.4/000077500000000000000000000000001436677633700140235ustar00rootroot00000000000000python-ipmi-0.5.4/.coveragerc000066400000000000000000000001021436677633700161350ustar00rootroot00000000000000[report] omit = */python?.?/* */site-packages/nose/* */tests/* python-ipmi-0.5.4/.github/000077500000000000000000000000001436677633700153635ustar00rootroot00000000000000python-ipmi-0.5.4/.github/workflows/000077500000000000000000000000001436677633700174205ustar00rootroot00000000000000python-ipmi-0.5.4/.github/workflows/publish.yml000066400000000000000000000014211436677633700216070ustar00rootroot00000000000000name: Publish Python Package on PYPI on: push jobs: deploy: name: Publish to Pypi runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Set up Python 3.7 uses: actions/setup-python@v4 with: python-version: 3.7 - name: Install pypa/build run: >- python -m pip install build --user - name: Build a binary wheel and a source tarball run: >- python -m build --sdist --wheel --outdir dist/ . - name: Publish distribution to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_API_TOKEN }} python-ipmi-0.5.4/.github/workflows/test.yml000066400000000000000000000025361436677633700211300ustar00rootroot00000000000000name: Test on: [push, pull_request, workflow_dispatch] jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-22.04 strategy: matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.9"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: pip install python-coveralls coverage pytest pytest-cov future mock flake8 # - name: Setup and run tests # run: python setup.py test - name: Lint with flake8 run: | flake8 pyipmi/ tests/ --count --show-source --statistics - name: Test with pytest run: | pytest --cov=pyipmi --cov-report=lcov - name: Coveralls Parallel uses: coverallsapp/github-action@master with: github-token: ${{ secrets.github_token }} flag-name: run-${{ matrix.test_number }} parallel: true path-to-lcov: coverage.lcov finish: needs: tests runs-on: ubuntu-22.04 steps: - name: Coveralls Finished uses: coverallsapp/github-action@master with: github-token: ${{ secrets.github_token }} parallel-finished: true python-ipmi-0.5.4/.gitignore000066400000000000000000000000651436677633700160140ustar00rootroot00000000000000*~ *.pyc *.swp build/ dist/ *.egg-info/ .eggs/ .venv/python-ipmi-0.5.4/AUTHORS000066400000000000000000000001221436677633700150660ustar00rootroot00000000000000Michael Walle Heiko Thiery python-ipmi-0.5.4/COPYING000066400000000000000000000576461436677633700151000ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS python-ipmi-0.5.4/README.rst000066400000000000000000000131441436677633700155150ustar00rootroot00000000000000Pure Python IPMI Library ======================== |BuildStatus| |PyPiVersion| |Documentation| |PyPiPythonVersions| |Coveralls| |CodeClimate| |Codacy| Features -------- * native RMCP interface * legacy RMCP interface (using ipmitool as backend) * RMCP+ interface (using ipmitool as backend) * system (KCS) interface (using ipmitool as backend) * IPMB interface using the `Total Phase`_ Aardvark * IPMB interface using ipmb-dev driver on Linux Tested Devices -------------- * Kontron mTCA Carrier Manager * Kontron CompactPCI boards * Pigeon Point Shelf Manager * HPE iLO3/iLO4 * N.A.T. NAT-MCH * DESY MMC STAMP & related AMCs (DAMC-FMC2ZUP, DAMC-FMC1Z7IO) Requirements ------------ For IPMB interface a `Total Phase`_ Aardvark is needed. Another option is to use ipmb-dev driver on Linux with an I2C bus, driver of which supports slave mode: https://www.kernel.org/doc/html/latest/driver-api/ipmb.html For legacy RMCP, RMCP+ and system interface (KCS) the installtion of ipmitool is required. Installation ------------ Using ``pip`` ''''''''''''' The recommended installation method is using `pip `__:: pip install python-ipmi Manual installation ''''''''''''''''''' Download the source distribution package for the library. Extract the the package to a temporary location and install:: python setup.py install Documentation ------------- You can find the most up to date documentation at: http://python-ipmi.rtfd.org Example ------- Below is an example that shows how to setup the interface and the connection using the `ipmitool`_ as backend with both network and serial interfaces. Example with lan interface: .. code:: python import pyipmi import pyipmi.interfaces # Supported interface_types for ipmitool are: 'lan' , 'lanplus', and 'serial-terminal' interface = pyipmi.interfaces.create_interface('ipmitool', interface_type='lan') connection = pyipmi.create_connection(interface) connection.target = pyipmi.Target(0x82) connection.target.set_routing([(0x81,0x20,0),(0x20,0x82,7)]) connection.session.set_session_type_rmcp('10.0.0.1', port=623) connection.session.set_auth_type_user('admin', 'admin') connection.session.establish() connection.get_device_id() ipmitool command: .. code:: shell ipmitool -I lan -H 10.0.0.1 -p 623 -U "admin" -P "admin" -t 0x82 -b 0 -l 0 raw 0x06 0x01 Example with serial interface: .. code:: python import pyipmi import pyipmi.interfaces interface = pyipmi.interfaces.create_interface('ipmitool', interface_type='serial-terminal') connection = pyipmi.create_connection(interface) connection.target = pyipmi.Target(0xb2) # set_session_type_serial(port, baudrate) connection.session.set_session_type_serial('/dev/tty2', 115200) connection.session.establish() connection.get_device_id() ipmitool command: .. code:: shell ipmitool -I serial-terminal -D /dev/tty2:115200 -t 0xb2 -l 0 raw 0x06 0x01 Compatibility ------------- Python > 3.6 is currently supported. Python 2.x is deprecated. Contributing ------------ Contributions are always welcome. You may send patches directly (eg. ``git send-email``), do a github pull request or just file an issue. * respect the coding style (eg. PEP8), * provide well-formed commit message (see `this blog post `_.) * add a Signed-off-by line (eg. ``git commit -s``) License ------- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .. _Total Phase: http://www.totalphase.com .. _ipmitool: http://sourceforge.net/projects/ipmitool/ .. |BuildStatus| image:: https://github.com/kontron/python-ipmi/actions/workflows/test.yml/badge.svg :target: https://github.com/kontron/python-ipmi/actions/workflows/test.yml .. |PyPiVersion| image:: https://badge.fury.io/py/python-ipmi.svg :target: http://badge.fury.io/py/python-ipmi .. |Documentation| image:: https://readthedocs.org/projects/python-ipmi/badge/?version=latest :target: https://python-ipmi.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. |PyPiPythonVersions| image:: https://img.shields.io/pypi/pyversions/python-ipmi.svg :alt: Python versions :target: http://badge.fury.io/py/python-ipmi .. |CodeClimate| image:: https://codeclimate.com/github/kontron/python-ipmi/badges/gpa.svg :target: http://codeclimate.com/github/kontron/python-ipmi .. |Coveralls| image:: https://coveralls.io/repos/github/kontron/python-ipmi/badge.svg?branch=master :target: https://coveralls.io/github/kontron/python-ipmi?branch=master .. |Codacy| image:: https://app.codacy.com/project/badge/Grade/068eca4b1e784425aa46ae0b06aeaf37 :alt: Codacy Badge :target: https://www.codacy.com/gh/kontron/python-ipmi/dashboard?utm_source=github.com&utm_medium=referral&utm_content=kontron/python-ipmi&utm_campaign=Badge_Grade python-ipmi-0.5.4/bin/000077500000000000000000000000001436677633700145735ustar00rootroot00000000000000python-ipmi-0.5.4/bin/supported_cmds.py000066400000000000000000000032521436677633700202020ustar00rootroot00000000000000import sys from collections import OrderedDict, namedtuple from pyipmi.msgs.registry import DEFAULT_REGISTRY def make_table(grid): col_length = map(list, zip(*[[len(item) for item in row] for row in grid])) max_cols = [max(out) for out in col_length] rst = table_div(max_cols, 1) for i, row in enumerate(grid): header_flag = False if i == 0 or i == len(grid)-1: header_flag = True rst += normalize_row(row, max_cols) rst += table_div(max_cols, header_flag) return rst def table_div(max_cols, header_flag=1): out = "" if header_flag == 1: style = "=" else: style = "-" for max_col in max_cols: out += max_col * style + " " out += "\n" return out def normalize_row(row, max_cols): r = "" for i, max_col in enumerate(max_cols): r += row[i] + (max_col - len(row[i]) + 1) * " " return r + "\n" def get_command_list(): data = list() Command = namedtuple('Command', ['netfn', 'cmdid', 'grpext', 'name']) od = OrderedDict(sorted(DEFAULT_REGISTRY.registry.items())) for key, val in od.items(): if isinstance(key, tuple): # skip response messages if key[0] & 1: continue data.append(Command(str(hex(key[0])), str(hex(key[1])), str(key[2]), val.__name__[:-3])) return data def main(): data = get_command_list() data.insert(0, ('Netfn', 'CMD', 'Group Extension', 'Name')) if len(sys.argv) > 1 and sys.argv[1].lower() == 'rst': rst = make_table(data) print(rst) else: print(data) if __name__ == '__main__': main() python-ipmi-0.5.4/docs/000077500000000000000000000000001436677633700147535ustar00rootroot00000000000000python-ipmi-0.5.4/docs/commands.rst000066400000000000000000000313241436677633700173110ustar00rootroot00000000000000===== ==== =============== ==================================== Netfn CMD Group Extension Name ===== ==== =============== ==================================== 0x0 0x0 None GetChassisCapabilities ----- ---- --------------- ------------------------------------ 0x0 0x1 None GetChassisStatus ----- ---- --------------- ------------------------------------ 0x0 0x2 None ChassisControl ----- ---- --------------- ------------------------------------ 0x0 0xf None GetPohCounter ----- ---- --------------- ------------------------------------ 0x4 0x0 None SetEventReceiver ----- ---- --------------- ------------------------------------ 0x4 0x1 None GetEventReceiver ----- ---- --------------- ------------------------------------ 0x4 0x20 None GetDeviceSdrInfo ----- ---- --------------- ------------------------------------ 0x4 0x21 None GetDeviceSdr ----- ---- --------------- ------------------------------------ 0x4 0x22 None ReserveDeviceSdrRepository ----- ---- --------------- ------------------------------------ 0x4 0x24 None SetSensorHysteresis ----- ---- --------------- ------------------------------------ 0x4 0x25 None GetSensorHysteresis ----- ---- --------------- ------------------------------------ 0x4 0x26 None SetSensorThresholds ----- ---- --------------- ------------------------------------ 0x4 0x27 None GetSensorThresholds ----- ---- --------------- ------------------------------------ 0x4 0x28 None SetSensorEventEnable ----- ---- --------------- ------------------------------------ 0x4 0x29 None GetSensorEventEnable ----- ---- --------------- ------------------------------------ 0x4 0x2a None RearmSensorEvents ----- ---- --------------- ------------------------------------ 0x4 0x2d None GetSensorReading ----- ---- --------------- ------------------------------------ 0x6 0x1 None GetDeviceId ----- ---- --------------- ------------------------------------ 0x6 0x2 None ColdReset ----- ---- --------------- ------------------------------------ 0x6 0x3 None WarmReset ----- ---- --------------- ------------------------------------ 0x6 0x4 None GetSelftestResults ----- ---- --------------- ------------------------------------ 0x6 0x5 None ManufacturingTestOn ----- ---- --------------- ------------------------------------ 0x6 0x22 None ResetWatchdogTimer ----- ---- --------------- ------------------------------------ 0x6 0x24 None SetWatchdogTimer ----- ---- --------------- ------------------------------------ 0x6 0x25 None GetWatchdogTimer ----- ---- --------------- ------------------------------------ 0x6 0x2e None SetBmcGlobalEnables ----- ---- --------------- ------------------------------------ 0x6 0x2f None GetBmcGlobalEnables ----- ---- --------------- ------------------------------------ 0x6 0x30 None ClearMessageFlags ----- ---- --------------- ------------------------------------ 0x6 0x31 None GetMessageFlags ----- ---- --------------- ------------------------------------ 0x6 0x32 None EnableMessageChannelReceive ----- ---- --------------- ------------------------------------ 0x6 0x33 None GetMessage ----- ---- --------------- ------------------------------------ 0x6 0x34 None SendMessage ----- ---- --------------- ------------------------------------ 0x6 0x35 None ReadEventMessageBuffer ----- ---- --------------- ------------------------------------ 0x6 0x38 None GetChannelAuthenticationCapabilities ----- ---- --------------- ------------------------------------ 0x6 0x39 None GetSessionChallenge ----- ---- --------------- ------------------------------------ 0x6 0x3a None ActivateSession ----- ---- --------------- ------------------------------------ 0x6 0x3b None SetSessionPrivilegeLevel ----- ---- --------------- ------------------------------------ 0x6 0x3c None CloseSession ----- ---- --------------- ------------------------------------ 0x6 0x52 None MasterWriteRead ----- ---- --------------- ------------------------------------ 0xa 0x10 None GetFruInventoryAreaInfo ----- ---- --------------- ------------------------------------ 0xa 0x11 None ReadFruData ----- ---- --------------- ------------------------------------ 0xa 0x12 None WriteFruData ----- ---- --------------- ------------------------------------ 0xa 0x20 None GetSdrRepositoryInfo ----- ---- --------------- ------------------------------------ 0xa 0x21 None GetSdrRepositoryAllocationInfo ----- ---- --------------- ------------------------------------ 0xa 0x22 None ReserveSdrRepository ----- ---- --------------- ------------------------------------ 0xa 0x23 None GetSdr ----- ---- --------------- ------------------------------------ 0xa 0x24 None AddSdr ----- ---- --------------- ------------------------------------ 0xa 0x25 None PartialAddSdr ----- ---- --------------- ------------------------------------ 0xa 0x26 None DeleteSdr ----- ---- --------------- ------------------------------------ 0xa 0x27 None ClearSdrRepository ----- ---- --------------- ------------------------------------ 0xa 0x2c None RunInitializationAgent ----- ---- --------------- ------------------------------------ 0xa 0x40 None GetSelInfo ----- ---- --------------- ------------------------------------ 0xa 0x41 None GetSelAllocationInfo ----- ---- --------------- ------------------------------------ 0xa 0x42 None ReserveSel ----- ---- --------------- ------------------------------------ 0xa 0x43 None GetSelEntry ----- ---- --------------- ------------------------------------ 0xa 0x44 None AddSelEntry ----- ---- --------------- ------------------------------------ 0xa 0x46 None DeleteSelEntry ----- ---- --------------- ------------------------------------ 0xa 0x47 None ClearSel ----- ---- --------------- ------------------------------------ 0xa 0x48 None GetSelTime ----- ---- --------------- ------------------------------------ 0xa 0x49 None SetSelTime ----- ---- --------------- ------------------------------------ 0xc 0x1 None SetLanConfigurationParameters ----- ---- --------------- ------------------------------------ 0xc 0x2 None GetLanConfigurationParameters ----- ---- --------------- ------------------------------------ 0x2c 0x0 0 GetPicmgProperties ----- ---- --------------- ------------------------------------ 0x2c 0x1 0 GetAddressInfo ----- ---- --------------- ------------------------------------ 0x2c 0x2 0 GetShelfAddressInfo ----- ---- --------------- ------------------------------------ 0x2c 0x4 0 FruControl ----- ---- --------------- ------------------------------------ 0x2c 0x5 0 GetFruLedProperties ----- ---- --------------- ------------------------------------ 0x2c 0x6 0 GetFruLedColorCapabilities ----- ---- --------------- ------------------------------------ 0x2c 0x7 0 SetFruLedState ----- ---- --------------- ------------------------------------ 0x2c 0x8 0 GetFruLedState ----- ---- --------------- ------------------------------------ 0x2c 0xa 0 SetFruActivationPolicy ----- ---- --------------- ------------------------------------ 0x2c 0xb 0 GetFruActivationPolicy ----- ---- --------------- ------------------------------------ 0x2c 0xc 0 SetFruActivation ----- ---- --------------- ------------------------------------ 0x2c 0xd 0 GetDeviceLocatorRecordId ----- ---- --------------- ------------------------------------ 0x2c 0xe 0 SetPortState ----- ---- --------------- ------------------------------------ 0x2c 0xf 0 GetPortState ----- ---- --------------- ------------------------------------ 0x2c 0x12 0 GetPowerLevel ----- ---- --------------- ------------------------------------ 0x2c 0x14 0 GetFanSpeedProperties ----- ---- --------------- ------------------------------------ 0x2c 0x15 0 SetFanLevel ----- ---- --------------- ------------------------------------ 0x2c 0x16 0 GetFanLevel ----- ---- --------------- ------------------------------------ 0x2c 0x1e 0 GetFruControlCapabilities ----- ---- --------------- ------------------------------------ 0x2c 0x23 0 GetLocationInformation ----- ---- --------------- ------------------------------------ 0x2c 0x24 0 SendPowerChannelControl ----- ---- --------------- ------------------------------------ 0x2c 0x25 0 GetPowerChannelStatus ----- ---- --------------- ------------------------------------ 0x2c 0x28 0 SendPmHeartbeat ----- ---- --------------- ------------------------------------ 0x2c 0x29 0 GetTelcoAlarmCapability ----- ---- --------------- ------------------------------------ 0x2c 0x2e 0 GetTargetUpgradeCapabilities ----- ---- --------------- ------------------------------------ 0x2c 0x2f 0 GetComponentProperties ----- ---- --------------- ------------------------------------ 0x2c 0x30 0 AbortFirmwareUpgrade ----- ---- --------------- ------------------------------------ 0x2c 0x31 0 InitiateUpgradeAction ----- ---- --------------- ------------------------------------ 0x2c 0x32 0 UploadFirmwareBlock ----- ---- --------------- ------------------------------------ 0x2c 0x33 0 FinishFirmwareUpload ----- ---- --------------- ------------------------------------ 0x2c 0x34 0 GetUpgradeStatus ----- ---- --------------- ------------------------------------ 0x2c 0x35 0 ActivateFirmware ----- ---- --------------- ------------------------------------ 0x2c 0x36 0 QuerySelftestResults ----- ---- --------------- ------------------------------------ 0x2c 0x37 0 QueryRollbackStatus ----- ---- --------------- ------------------------------------ 0x2c 0x38 0 InitiateManualRollback ----- ---- --------------- ------------------------------------ 0x2c 0x3b 0 SetSignalingClass ----- ---- --------------- ------------------------------------ 0x2c 0x3c 0 GetSignalingClass ===== ==== =============== ==================================== python-ipmi-0.5.4/docs/source/000077500000000000000000000000001436677633700162535ustar00rootroot00000000000000python-ipmi-0.5.4/docs/source/bmcWatchdog_cmd.rst000066400000000000000000000102171436677633700220530ustar00rootroot00000000000000BMC Watchdog Timer Commands =========================== The :abbr:`BMC (Board Management Controller)` implements a standardized **'Watchdog Timer'** that can be used for a number of system timeout functions by system management software or by the :abbr:`BIOS (Basic Input Output System)`. Setting a timeout value of '0' allows the selected timeout action to occur immediately. This provides a standardized means for devices on the :abbr:`IPMB (Intelligent Platform Management Bus)` to performs emergency recovery actions. The `IPMI standard`_ defines the following BMC Watchdog Timer commands: +-------------------------------+-----+---------+-----+ | Command | O/M | Support | API | +===============================+=====+=========+=====+ | Reset Watchdog Timer | M | Yes | Yes | +-------------------------------+-----+---------+-----+ | Set Watchdog Timer | M | Yes | Yes | +-------------------------------+-----+---------+-----+ | Get Watchdog Timer | M | Yes | Yes | +-------------------------------+-----+---------+-----+ .. note:: - O/M - Optional/Mandatory command as stated by the IPMI standard - Support - Supported command by **send_message_with_name** method - API - High level API support implemented in this library Reset Watchdog Timer Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used for starting and restarting the **Watchdog Timer** from the initial countdown value that was specified with ``set_watchdog_timer`` method (see next command). +------------------------------+ | **reset_watchdog_timer()** | +------------------------------+ For example: .. code:: python ipmi.reset_watchdog_timer() Set Watchdog Timer Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to initialize and configure the **Watchdog Timer**. This command is also used for stopping the timer. +----------------------------------------------+ | **set_watchdog_timer(watchdog_timer)** | +----------------------------------------------+ For example: .. code:: python ipmi.set_watchdog_timer(watchdog_timer) where the ``watchdog_timer`` has the attributes shown bellow for the next command. Get Watchdog Timer Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command retrieves the current settings and present countdown of the watchdog timer. +------------------------------+ | **get_watchdog_timer()** | +------------------------------+ where the returned object has the following attributes shown in the order as they appear in the table of the `IPMI standard`_: * ``dont_log`` * ``is_running`` (``dont_stop``) * ``timer_use`` * ``pre_timeout_interrupt`` * ``timeout_action`` * ``pre_timeout_interval`` * ``timer_use_expiration_flags`` * ``initial_countdown`` * ``present_countdown`` The ``dont_stop`` attribute is not changed by the ``get_watchdog_timer`` method and used only by the ``set_watchdog_timer`` method. For example: .. code:: python watchdog_timer=ipmi.get_watchdog_timer() print("--- Printing Watchdog Timer ---") timer_use_const=['BIOS FRB2','BIOS/POST','OS Load','SMS/OS','OEM'] pretime_intr_const=['None','SMI','NMI','Msg intr'] timeout_act_const=['No action','Hard Reset','Power Down','Power Cycle'] print(""" Don't log: {0[dont_log]:} Timer is running: {0[is_running]:} Pre-timout interval: {0[pre_timeout_interval]:d} Initial countdown value: {0[initial_countdown]:d} Present countdown value: {0[present_countdown]:d} """[1:-1].format(wd_timer.__dict__),end='') print(" Timer use: ", timer_use_const[watchdog_timer.__dict__['timer_use']-1]) print(" Timer use expiration flag: ", timer_use_const[watchdog_timer.__dict__['timer_use_expiration_flags']-1]) print(" Pre-timeout interrupt: ", pretime_intr_const[watchdog_timer.__dict__['pre_timeout_interval']]) print(" Time out action: ", timeout_act_const[watchdog_timer.__dict__['timeout_action']]) .. _IPMI standard: https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf python-ipmi-0.5.4/docs/source/chassis_cmd.rst000066400000000000000000000107621436677633700212730ustar00rootroot00000000000000Chassis Commands ================ These commands are primarily to provide standardized chassis status and control functions for Remote Management Cards and Remote Consoles that access the :abbr:`BMC (Board Management Controller)`. The `IPMI standard`_ defines the following Chassis commands: +-------------------------------+-----+---------+-----+ | Command | O/M | Support | API | +===============================+=====+=========+=====+ | Get Chassis Capabilities | M | Yes | No | +-------------------------------+-----+---------+-----+ | Get Chassis Status | M | Yes | Yes | +-------------------------------+-----+---------+-----+ | Chassis Control | M | Yes | Yes | +-------------------------------+-----+---------+-----+ | Chassis Reset | O | No | No | +-------------------------------+-----+---------+-----+ | Chassis Identify | O | No | No | +-------------------------------+-----+---------+-----+ | Set Front Panel Enables | O | No | No | +-------------------------------+-----+---------+-----+ | Set Chassis Capabilities | O | No | No | +-------------------------------+-----+---------+-----+ | Set Power Restore Policy | O | No | No | +-------------------------------+-----+---------+-----+ | Set Power Cycle Interval | O | No | No | +-------------------------------+-----+---------+-----+ | Get System Restart Cause | O | No | No | +-------------------------------+-----+---------+-----+ | Set System Boot Options | O | No | No | +-------------------------------+-----+---------+-----+ | Get System Boot Options | O | No | No | +-------------------------------+-----+---------+-----+ | Get POH Counter | O | Yes | No | +-------------------------------+-----+---------+-----+ .. note:: - O/M - Optional/Mandatory command as stated by the IPMI standard - Support - Supported command by **send_message_with_name** method - API - High level API support implemented in this library Get Chassis Capabilities Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command returns information about which main chassis management functions are present on the :abbr:`IPMB (Intelligent Platform Management Bus)` and what addresses are used to access those functions. This command is used to find the devices that provide functions such as :abbr:`SEL (System Event Log)`, :abbr:`SDR (Snesor Data Record)`, and :abbr:`ICMB (Intelligent Chassis Management Bus)` Bridging so that theyt can be accessed via commands delivered via a physical or logical :abbr:`IPMB (Intelligent Platform Management Bus)`. +-------------------------------------+ | **get_chassis_capabilities()** | +-------------------------------------+ **NOT IMPLEMENTED YET!!!** Get Chassis Status Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command returns information regarding the high-level status of the system chassis and main power subsystem. +--------------------------------------+ | **get_chassis_status()** | +--------------------------------------+ where the returned object has the following attributes shown in the order as they appear in the table of the `IPMI standard`_: * ``restore_policy`` * ``control_fault`` * ``fault`` * ``interlock`` * ``overload`` * ``power_on`` * ``last_event`` * ``chassis_state`` For example: .. code:: python chassis_status=ipmi.get_chassis_status() Chassis Control Command ~~~~~~~~~~~~~~~~~~~~~~~ This command provides a mechanism for providing power up, power down, and reset control. +-----------------------------------------+ | **chassis_control(option)** | +-----------------------------------------+ where the ``option`` argument can take the following integer values as defined in the standard: - CONTROL_POWER_DOWN = 0 - CONTROL_POWER_UP = 1 - CONTROL_POWER_CYCLE = 2 - CONTROL_HARD_RESET = 3 - CONTROL_DIAGNOSTIC_INTERRUPT = 4 - CONTROL_SOFT_SHUTDOWN = 5 For example: .. code:: python ipmi.chassis_control(option) There are methods defined for each of the above options: .. code:: python ipmi.chassis_control_power_down() ipmi.chassis_control_power_up() ipmi.chassis_control_power_cycle() ipmi.chassis_control_hard_reset() ipmi.chassis_control_diagnostic_interrupt() ipmi.chassis_control_soft_shutdown() .. _IPMI standard: https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf python-ipmi-0.5.4/docs/source/conf.py000066400000000000000000000117601436677633700175570ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # python-ipmi documentation build configuration file, created by # sphinx-quickstart on Fri Mar 1 16:02:43 2019. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', 'sphinx.ext.graphviz'] # Add any paths that contain templates here, relative to this directory. templates_path = ['ytemplates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'python-ipmi' copyright = '2019, FerencFarkasPhD' author = 'FerencFarkasPhD' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.1' # The full version, including alpha/beta/rc tags. release = '0.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['ystatic'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'python-ipmidoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'python-ipmi.tex', 'python-ipmi Documentation', 'FerencFarkasPhD', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'python-ipmi', 'python-ipmi Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'python-ipmi', 'python-ipmi Documentation', author, 'python-ipmi', 'One line description of project.', 'Miscellaneous'), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'https://docs.python.org/': None} python-ipmi-0.5.4/docs/source/index.rst000066400000000000000000000011641436677633700201160ustar00rootroot00000000000000.. python-ipmi documentation master file, created by sphinx-quickstart on Fri Mar 1 16:02:43 2019. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to python-ipmi's documentation! ======================================= This documentation describes the usage of the python-ipmi library. .. toctree:: :maxdepth: 2 :caption: Contents: introduction quick_start ipmDevGlobal_cmd ipmiMsgSupport_cmd bmcWatchdog_cmd chassis_cmd Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-ipmi-0.5.4/docs/source/introduction.rst000066400000000000000000000071401436677633700215300ustar00rootroot00000000000000Introduction ============ The :abbr:`IPMI (Intelligent Platform Management Interface)` is a set of computer interface specifications for an autonomous computer subsystem that provides management and monitoring capabilities independently of the host system's :abbr:`CPU (Central Processor Unit)`, firmware (:abbr:`BIOS (Basic Input/Output System)` or :abbr:`UEFI (Unified Extensible Firmware Interface)`) and operating system. The python-ipmi library provides :abbr:`API (Application Programming Interface)` for using IPMI protocol within the python environment. This library supports :abbr:`IPMI (Intelligent Platform Management Interface)` version 2.0 as described in the `IPMI standard`_. There are two ways to communicate with a server using :abbr:`IPMI (Intelligent Platform Management Interface)` interface: 1. :abbr:`IPMI (Intelligent Platform Management Interface)` over :abbr:`LAN (Local Area Network)` using :abbr:`RMCP (Remote Management Control Protocol)` packet datagrams 2. :abbr:`IPMB (Intelligent Platform Management Bus)` is an |I2C| -based bus Features -------- * native :abbr:`RMCP (Remote Management Control Protocol)` interface (using python libraries only) * legacy :abbr:`RMCP (Remote Management Control Protocol)` interface (requires `ipmitool`_ to be installed) * :abbr:`IPMB (Intelligent Platform Management Bus)` interface (using the `Total Phase`_ Aardvark) Tested Devices -------------- * Kontron mTCA Carrier Manager * Kontron CompactPCI boards * Pigeon Point Shelf Manager * HPE iLO3/iLO4 and T5224DN 2U24 Requirements ------------ For :abbr:`IPMB (Intelligent Platform Management Bus)` interface a `Total Phase`_ Aardvark is needed. Installation ------------ Using ``pip`` ''''''''''''' The recommended installation method is using `pip `__:: pip install python-ipmi .. warning:: If you are using Anaconda, still the above installation procedure shall be used as **conda install python-ipmi** will not find the installation package. Manual installation ''''''''''''''''''' Download the source distribution package for the library. Extract the package to a temporary location and install:: python setup.py install Compatibility ------------- Python 2.7 is currently supported. Python 3.x support is in beta Contributing ------------ Contributions are always welcome. You may send patches directly (eg. ``git send-email``), do a github pull request or just file an issue. * respect the coding style (eg. PEP8), * provide well-formed commit message (see `this blog post `_.) * add a Signed-off-by line (eg. ``git commit -s``) License ------- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .. _Total Phase: http://www.totalphase.com .. _ipmitool: http://sourceforge.net/projects/ipmitool/ .. _IPMI standard: https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf .. |I2C| replace:: I\ :sup:`2`\ Cpython-ipmi-0.5.4/docs/source/ipmDevGlobal_cmd.rst000066400000000000000000000117041436677633700222000ustar00rootroot00000000000000IPM Device "Global" Commands ============================ This section describes the high level :abbr:`API (Application Programming Interface)` for the commands that are common to all :abbr:`IPM (Intelligent Platform Management)` devices that follow the :abbr:`IPMI (Intelligent Platform Management Interface)` standard. The `IPMI standard`_ defines the following IPM Device "Global" commands: +-------------------------------+-----+---------+-----+ | Command | O/M | Support | API | +===============================+=====+=========+=====+ | Get Device ID | M | Yes | Yes | +-------------------------------+-----+---------+-----+ | Cold Reset | O | Yes | Yes | +-------------------------------+-----+---------+-----+ | Warm Reset | O | Yes | Yes | +-------------------------------+-----+---------+-----+ | Get Self Test Results | M | Yes | No | +-------------------------------+-----+---------+-----+ | Manufacturing Test On | O | Yes | No | +-------------------------------+-----+---------+-----+ | Get ACPI Power State | O | No | No | +-------------------------------+-----+---------+-----+ | Set ACPI Power State | O | No | No | +-------------------------------+-----+---------+-----+ | Get Device GUID | O | No | No | +-------------------------------+-----+---------+-----+ .. note:: - O/M - Optional/Mandatory command as stated by the IPMI standard - Support - Supported command by **send_message_with_name** method - API - High level API support implemented in this library Get Device ID Command ~~~~~~~~~~~~~~~~~~~~~ You can retrieve the Intelligent Devices's HW revision, FW/SW revision, and information regarding additional logical device functionality with +------------------------------+ | **get_device_id()** | +------------------------------+ where the returned object has the following attributes shown in the order as appear in the table of the `IPMI standard`_: * ``device_id`` * ``provides_sdrs`` * ``revision`` * ``available`` * ``fw_revision (fw_revision.major, fw_revision.minor)`` * ``ipmi_version (ipmi_version.major, ipmi_version.minor)`` * ``supported_functions`` * ``manufacturer_id`` * ``product_id`` * ``aux`` The returned object also has a method ``supports_function(func_name)`` where the argument can be one of the following (not case sensitive): * **'sensor'** * **'sdr_repository'** * **'sel'** * **'fru_inventory'** * **'ipmb_event_receiver'** * **'ipmb_event_generator'** * **'bridge'** * **'chassis'** The method returns **'True'** if the given function is supported by the **Target**, otherwise is **'False'**. For example: .. code:: python device_id = ipmi.get_device_id() print("--- Printing Device ID ---") functions = ( ('SENSOR', 'Sensor Device', 11), ('SDR_REPOSITORY', 'SDR Repository Device', 3), ('SEL', 'SEL Device', 14), ('FRU_INVENTORY', 'FRU Inventory Device', 4), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver', 5), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator', 4), ('BRIDGE', 'Bridge', 18), ('CHASSIS', 'Chassis Device', 10)) ChkBox=['[ ]','[X]'] print(''' Device ID: %(device_id)s Provides Device SDRs: %(provides_sdrs)s Device Revision: %(revision)s Device Available: %(available)d Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Aux Firmware Rev Info: %(aux)s Additional Device Support: '''[1:-1] % device_id.__dict__) for n, s, l in functions: if device_id.supports_function(n): print(' %s%s%s' % (s,l*' ',ChkBox[1])) else: print(' %s%s%s' % (s,l*' ',ChkBox[0])) Cold Reset Command ~~~~~~~~~~~~~~~~~~ This command directs the **Target** to perform a 'Cold Reset' of itself. The device reinitalizes its event, communcation, and sensor funtioncs. Self Test, if implemented, will be also run. +------------------------------+ | **cold_reset()** | +------------------------------+ For example: .. code:: python ipmi.cold_reset() Warm Reset Command ~~~~~~~~~~~~~~~~~~ This command directs the **Target** to perform a 'Warm Reset' of itself. Communication interfaces are reset, but current configurations of interrupt enables, thresholds, etc. will be left alone, and no Self Test initiated. +------------------------------+ | **warm_reset()** | +------------------------------+ For example: .. code:: python ipmi.warm_reset() .. _IPMI standard: https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf python-ipmi-0.5.4/docs/source/ipmiMsgSupport_cmd.rst000066400000000000000000000172151436677633700226400ustar00rootroot00000000000000IPMI Messaging Support Commands =============================== This section describes the commands used to support the system messaging interfaces. This includes control bits for using the :abbr:`BMC (Board Management Controller)` as an Event receiver and :abbr:`SEL (System Event Log)` Device. :abbr:`SMM (System Management Mode)` Messaging and Event Message Buffer support is optional. The `IPMI standard`_ defines the following IPMI Messaging Support commands: +-----------------------------------------+-----+---------+-----+ | Command | O/M | Support | API | +=========================================+=====+=========+=====+ | Set BMC Global Enables | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Get BMC Global Enables | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Clear Message Flags | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Get Message Flags | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Enable Message Channel Receive | O | Yes | No | +-----------------------------------------+-----+---------+-----+ | Get Message | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Send Message | M | Yes | No | +-----------------------------------------+-----+---------+-----+ | Read Event Message Buffer | O | Yes | No | +-----------------------------------------+-----+---------+-----+ | Get System Interface Capabilities | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get BT Interface Capabilities | M | No | No | +-----------------------------------------+-----+---------+-----+ | Master Write-Read | M | Yes | Yes | +-----------------------------------------+-----+---------+-----+ | Get System GUID | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set System Info | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get System Info | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get Channel Authentication Capabilities | O | Yes | Yes | +-----------------------------------------+-----+---------+-----+ | Get Channel Cipher Suites | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get Session Challenge | O | Yes | No | +-----------------------------------------+-----+---------+-----+ | Activate Session | O | Yes | No | +-----------------------------------------+-----+---------+-----+ | Set Session Privilege Level | O | Yes | No | +-----------------------------------------+-----+---------+-----+ | Close Session | O | Yes | Yes | +-----------------------------------------+-----+---------+-----+ | Get Session Info | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get AuthCode | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set Channel Access | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get Channel Access | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get Channel Info | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set Channel Security Keys | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set User Access | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get User Access | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set User Name | O | No | No | +-----------------------------------------+-----+---------+-----+ | Get User Name | O | No | No | +-----------------------------------------+-----+---------+-----+ | Set User Password | O | No | No | +-----------------------------------------+-----+---------+-----+ .. note:: - O/M - Optional/Mandatory command as stated by the IPMI standard - Support - Supported command by **send_message_with_name** method - API - High level API support implemented in this library Establish Session ~~~~~~~~~~~~~~~~~ This is not equivalent with a single IPMI command, but represents a high level API function using several IPMI commands. It is used to establish a session between the **Slave** and the **Target**. The method +------------------------------+ | **establish()** | +------------------------------+ creates and activates a session of the ``ipmi.session`` instance with the given authentication and privilige level. Multiple IPMI commands are used to establish the session. The following steps are done during the session establishment for an RMCP interface: - ping the **Target** IP address - issue a **"Get Channel Authentication Capabilities"** command - issue a **"Get Session Challenge"** command - issue an **"Activate Session"** command - issue a **"Set Session Privilege Level"** command (privilige is set always to ADMINISTRATOR level) If ``keep_alive_interval`` argument for the interface instantiation was set to a nonzero value then the channel is kept alive by regularly sending the **"Get Device ID"** IPMI command. Example of establishing a session: .. code:: python ipmi.session.establsih() Get Channel Authentication Capabilities Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to retrieve capability information about a particular channel. +-----------------------------------------------------------------+ | **get_channel_authentication_capabilities(channel, priv_lvl)** | +-----------------------------------------------------------------+ You should pass the channel number to ``channel``, and the requested maximum privilige level to ``priv_lvl``. Example: .. code:: python ipmi.get_channel_authentication_capabilities(channel=0x0E,priv_lvl=1) Master Write-Read Command ~~~~~~~~~~~~~~~~~~~~~~~~~ This command can be used for low level |I2C|/SMBus write, read, or write-read accesses to the IPMB or private busses behind a management controller. The command can also be used for providing low-level access to devices that provide an SMBus slave interface. +---------------------------------------------------------------------------+ | **i2c_write_read(bus_type, bus_id, channel, address, count, data=None)** | +---------------------------------------------------------------------------+ Close Session Command ~~~~~~~~~~~~~~~~~~~~~ This command is used to immediately terminate a session in progress. The method +------------------------------+ | **close()** | +------------------------------+ closes the session of the ``ipmi.session`` instance. Example of closing a session: .. code:: python ipmi.session.close() .. _IPMI standard: https://www.intel.com/content/dam/www/public/us/en/documents/product-briefs/ipmi-second-gen-interface-spec-v2-rev1-1.pdf .. |I2C| replace:: I\ :sup:`2`\ C python-ipmi-0.5.4/docs/source/quick_start.rst000066400000000000000000000320011436677633700213320ustar00rootroot00000000000000Quick start =========== Importing the library --------------------- After you successfully installed the **python-ipmi** library, import it in your python environment: .. code:: python import pyipmi import pyipmi.interfaces Creating the session -------------------- Authenticated :abbr:`IPMI (Intelligent Platform Management Interface)` communication to the :abbr:`BMC (Board Management Controller)` is accomplished by establishing a session. Once established, a session is identified by a Session ID. The Session ID identifies a connection between a given remote user and :abbr:`BMC (Board Management Controller)`, using either the :abbr:`LAN (Local Area Network)` or Serial/Modem connection. Before establishing the session the interface type shall be defined. There are 4 interface types included in this library: * **'rmcp'** - using native :abbr:`RMCP (Remote Management Control Protocol)` encapsulation over :abbr:`LAN (Local Area Network)` (so called :abbr:`IPMI (Intelligent Platform Management Interface)` over :abbr:`LAN (Local Area Network)`). This interface requires only common python libraries. * **'ipmitool'** - so called legacy :abbr:`RMCP (Remote Management Control Protocol)`, still an :abbr:`IPMI (Intelligent Platform Management Interface)` over :abbr:`LAN (Local Area Network)`, but requires IPMITOOL as backend. This interface requires `ipmitool`_ compiled and installed, and each time an ipmitool command is issued a new session is established with the Target (left for legacy purpuses; used before native rmcp was not implemented yet). * **'aardvark'** - :abbr:`IPMB (Intelligent Platform Management Bus)` interface (using the `Total Phase`_ Aardvark) * **'mock'** - This interface uses the ipmitool raw command to "emulate" an :abbr:`RMCP (Remote Management Control Protocol)` session. It uses the session information to assemble the correct ipmitool parameters. Therefore, a session must be established before any request can be sent. Then you create an instance of the ``pyipmi.Ipmi`` object using the ``interface`` instance just created, and set also the required parameteres of the interface type. You should also set the :abbr:`IPMI (Intelligent Platform Management Interface)` **Target**, otherwise different runtime errors shall be expected later on when invoking methods of this library. Finally, you can try to establish a session. If there is a connection problem (no response), then you get the following error during session establishment: .. error:: timeout: timed out This runtime error occurs anytime with any method in case of no response. Native RMCP interface ********************* Here is an example to create a native :abbr:`RMCP (Remote Management Control Protocol)` interface: .. code:: python interface = pyipmi.interfaces.create_interface(interface='rmcp', slave_address=0x81, host_target_address=0x20, keep_alive_interval=1) ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp(host='10.0.114.199', port=623) ipmi.session.set_auth_type_user(username='admin', password='admin') ipmi.target = pyipmi.Target(ipmb_address=0x20) ipmi.session.establish() device_id = ipmi.get_device_id() # Below code used only to print out the device ID information print(''' Device ID: %(device_id)s Device Revision: %(revision)s Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Device Available: %(available)d Provides SDRs: %(provides_sdrs)d Additional Device Support: '''[1:-1] % device_id.__dict__) functions = ( ('SENSOR', 'Sensor Device'), ('SDR_REPOSITORY', 'SDR Repository Device'), ('SEL', 'SEL Device'), ('FRU_INVENTORY', 'FRU Inventory Device'), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver'), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator'), ('BRIDGE', 'Bridge'), ('CHASSIS', 'Chassis Device') ) for n, s in functions: if device_id.supports_function(n): print(' %s' % s) if device_id.aux is not None: print('Aux Firmware Rev Info: [%s]' % ( ' '.join('0x%02x' % d for d in device_id.aux))) For ``create_interface`` method the first argument tells that a native RMCP interface shall be created, while for the rest of the arguments the default values are shown. After creating an instance of the ``interface`` object the interface parameters shall be set with ``set_session_type_rmcp`` and ``set_auth_type_user`` methods of the session as shown above. If authentication fails during session establishment an error of the following form shall be expected: .. error:: CompletionCodeError: CompletionCodeError cc=0x81 desc=Unknown error description Legacy RMCP interface with IPMITOOL as backend ********************************************** An example showing how to setup the interface and the connection using the ipmitool as backend with network interface: .. code:: python interface = pyipmi.interfaces.create_interface(interface='ipmitool', interface_type='lan') ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp('10.0.0.1', port=623) ipmi.session.set_auth_type_user('admin', 'admin') ipmi.target = pyipmi.Target(ipmb_address=0x82, routing=[(0x81,0x20,0),(0x20,0x82,7)]) ipmi.session.establish() ipmi.get_device_id() where in the ``create_interface`` method the supported interface types for ipmitool are **'lan'** , **'lanplus'**, and **'serial-terminal'**. When setting the **Target**, the ``ipmb_address`` argument represents the :abbr:`IPMI (Intelligent Platform Management Interface)` target address, and ``routing`` argument represents the bridging information over which a target is reachable. The path is given as a list of tuples in the form (address, bridge_channel). Here are three examples to have a better understanding about the format of the routing: * **Example #1**: access to an :abbr:`ATCA (Advanced Telecommunication Computing Architecture)` blade in a chassis - slave = 0x81, target = 0x82 - routing = [(0x81,0x20,0),(0x20,0x82,None)] .. graphviz:: digraph g{ rankdir=LR; nd1 [label="0x81"] nd2 [label="0x20"] nd3 [label="0x82"] nd4 [label="0x20"] nd1 -> nd2 [label="channel=0"] nd2 -> nd3 -> nd4 subgraph cluster0 { label="Slave" nd1; } subgraph cluster3 { label="Chassis" subgraph cluster1 { label="ATCA Blade (Target)" nd3; nd4; } nd2; } } } * **Example #2**: access to an :abbr:`MMC (Module Management Controller)` of an :abbr:`AMC (Advanced Mezzanine Card)` plugged into a :abbr:`CM (Carrier Module)` in a :abbr:`uTCA (Micro Telecommunication Computing Architecture)`-:abbr:`MCH (MicroTCA Carrier Hub)` chassis with :abbr:`ShMC (Shelf Management Controller)` - slave = 0x81, target = 0x72 - routing = [(0x81,0x20,0),(0x20,0x82,7),(0x20,0x72,None)] .. graphviz:: digraph g{ rankdir=LR; nd1 [label="0x81"] nd2 [label="0x20"] nd3 [label="0x82"] nd4 [label="0x20"] nd5 [label="0x72"] nd1 -> nd2 [label="channel=0"] nd2 -> nd3 -> nd4 nd4 -> nd5 [label="channel=7"] subgraph cluster0 { label="Slave" nd1; } subgraph cluster3 { label="uTCA - MCH" subgraph cluster1 { label="CM" nd3; nd4; } subgraph cluster2 { label="ShMC" nd2; } } subgraph cluster5 { label="AMC (Target)" subgraph cluster4 { label="MMC" nd5; } } } * **Example #3**: access to an :abbr:`MMC (Module Management Controller)` of an :abbr:`AMC (Advanced Mezzanine Card)` plugged into :abbr:`ATCA (Advanced Telecommunication Computing Architecture)` :abbr:`AMC (Advanced Mezzanine Card)` carrier - slave = 0x81, target = 0x72 - routing = [(0x81,0x20,0),(0x20,0x8e,7),(0x20,0x80,None)] .. graphviz:: digraph g{ rankdir=LR; nd1 [label="0x81"] nd2 [label="0x20"] nd3 [label="0x8E"] nd4 [label="0x20"] nd5 [label="0x80"] nd1 -> nd2 [label="channel=0"] nd2 -> nd3 -> nd4 nd4 -> nd5 [label="channel=7"] subgraph cluster0 { label="Slave" nd1; } subgraph cluster3 { label="ATCA" subgraph cluster1 { label="AMC Carrier" nd3; nd4; } nd2; } subgraph cluster5 { label="AMC (Target)" subgraph cluster4 { label="MMC" nd5; } } } ipmitool command: .. code:: shell ipmitool -I lan -H 10.0.0.1 -p 623 -U "admin" -P "admin" -t 0x82 -b 0 -l 0 raw 0x06 0x01 An example that shows how to setup the interface and the connection using the ipmitool as backend with serial interfaces: .. code:: python interface = pyipmi.interfaces.create_interface(interface='ipmitool', interface_type='serial-terminal') ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_serial('/dev/tty2', 115200) ipmi.target = pyipmi.Target(0xb2) ipmi.session.establish() ipmi.get_device_id() ipmitool command: .. code:: shell ipmitool -I serial-terminal -D /dev/tty2:115200 -t 0xb2 -l 0 raw 0x06 0x01 IPMB with Aardvark ****************** For :abbr:`IPMB (Intelligent Platform Management Bus)` interface with Aardvark tool you should use the followig code: .. code:: python interface = pyipmi.interfaces.create_interface('aardvark', slave_address=0x20, serial_number='2237-523145') ipmi = pyipmi.create_connection(interface) ipmi.target = pyipmi.Target(ipmb_address=0xb4) device_id = ipmi.get_device_id() # Below code used only to print out the device ID information print(''' Device ID: %(device_id)s Device Revision: %(revision)s Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Device Available: %(available)d Provides SDRs: %(provides_sdrs)d Additional Device Support: '''[1:-1] % device_id.__dict__) functions = ( ('SENSOR', 'Sensor Device'), ('SDR_REPOSITORY', 'SDR Repository Device'), ('SEL', 'SEL Device'), ('FRU_INVENTORY', 'FRU Inventory Device'), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver'), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator'), ('BRIDGE', 'Bridge'), ('CHASSIS', 'Chassis Device') ) for n, s in functions: if device_id.supports_function(n): print(' %s' % s) if device_id.aux is not None: print('Aux Firmware Rev Info: [%s]' % ( ' '.join('0x%02x' % d for d in device_id.aux))) Sending IPMI commands --------------------- You can send an :abbr:`IPMI (Intelligent Platform Management Interface)` message using the predefined command name +------------------------------------------------------------+ | **send_message_with_name(name, *args, **kwargs)** | +------------------------------------------------------------+ where the ``name`` argument represents the string name of the command as listed in the last column of table from `commands`_. For commands which do not require data to be sent name is the only argument to be passed. The returned value is on object which types depend on the name of the issued command. The following example requests the device ID: .. code:: python ipmi.send_message_with_name('GetDeviceId') .. note:: The returned object in this case is different from the one shown for the native :abbr:`RMCP (Remote Management Control Protocol)` example shown above. Closing the session ------------------- When you finish, close your session with ``session_close`` method. .. code:: python ipmi.session.close() As a beginner, you might find useful the debugging capabilities of this library. You can use the logging of the **python-ipmi** library by setting the following lines: .. code:: python import logging logging.basicConfig(filename='ipmi_debug.log', filemode='w', level=logging.DEBUG) in which case debug, info and warning messages are all recorded in the **'ipmi_debug.log'** file. .. note:: It is assumed in all code examples that the instantiation of the ``pyipmi.Ipmi`` object is called **ipmi**, thus **ipmi** will preceed all the methods and attributes of the ``pyipmi.Ipmi`` object. .. _Total Phase: http://www.totalphase.com .. _ipmitool: http://sourceforge.net/projects/ipmitool/ .. _commands: https://github.com/kontron/python-ipmi/blob/master/docs/commands.rst python-ipmi-0.5.4/examples/000077500000000000000000000000001436677633700156415ustar00rootroot00000000000000python-ipmi-0.5.4/examples/dcmi.py000066400000000000000000000026261436677633700171350ustar00rootroot00000000000000#!/usr/bin/env python import sys import pyipmi import pyipmi.interfaces def main(): if len(sys.argv) < 4: print(' ') sys.exit(1) host = sys.argv[1] user = sys.argv[2] password = sys.argv[3] interface = pyipmi.interfaces.create_interface('ipmitool', interface_type='lanplus') ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp(host, 623) ipmi.session.set_auth_type_user(user, password) ipmi.session.establish() ipmi.target = pyipmi.Target(ipmb_address=0x20) for selector in range(1, 6): caps = ipmi.get_dcmi_capabilities(selector) print('Selector: {} '.format(selector)) print(' version: {} '.format(caps.specification_conformence)) print(' revision: {}'.format(caps.parameter_revision)) print(' data: {}'.format(caps.parameter_data)) rsp = ipmi.get_power_reading(1) print('Power Reading') print(' current: {}'.format(rsp.current_power)) print(' minimum: {}'.format(rsp.minimum_power)) print(' maximum: {}'.format(rsp.maximum_power)) print(' average: {}'.format(rsp.average_power)) print(' timestamp: {}'.format(rsp.timestamp)) print(' period: {}'.format(rsp.period)) print(' state: {}'.format(rsp.reading_state)) if __name__ == '__main__': main() python-ipmi-0.5.4/examples/interface_aardvark.py000066400000000000000000000025551436677633700220350ustar00rootroot00000000000000#!/usr/bin/env python import pyipmi import pyipmi.interfaces interface = pyipmi.interfaces.create_interface('aardvark', slave_address=0x20, serial_number='2237-523145') ipmi = pyipmi.create_connection(interface) ipmi.target = pyipmi.Target(ipmb_address=0xb4) device_id = ipmi.get_device_id() print(''' Device ID: %(device_id)s Device Revision: %(revision)s Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Device Available: %(available)d Provides SDRs: %(provides_sdrs)d Additional Device Support: '''[1:-1] % device_id.__dict__) functions = ( ('SENSOR', 'Sensor Device'), ('SDR_REPOSITORY', 'SDR Repository Device'), ('SEL', 'SEL Device'), ('FRU_INVENTORY', 'FRU Inventory Device'), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver'), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator'), ('BRIDGE', 'Bridge'), ('CHASSIS', 'Chassis Device') ) for n, s in functions: if device_id.supports_function(n): print(' %s' % s) if device_id.aux is not None: print('Aux Firmware Rev Info: [%s]' % ( ' '.join('0x%02x' % d for d in device_id.aux))) python-ipmi-0.5.4/examples/interface_rmcp.py000066400000000000000000000031051436677633700211730ustar00rootroot00000000000000#!/usr/bin/env python import pyipmi import pyipmi.interfaces interface = pyipmi.interfaces.create_interface('rmcp', slave_address=0x81, host_target_address=0x20, keep_alive_interval=0) ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp('10.0.114.199', 623) ipmi.session.set_auth_type_user('admin', 'admin') ipmi.session.establish() ipmi.target = pyipmi.Target(ipmb_address=0x20) device_id = ipmi.get_device_id() ipmi.session.close() print(''' Device ID: %(device_id)s Device Revision: %(revision)s Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Device Available: %(available)d Provides SDRs: %(provides_sdrs)d Additional Device Support: '''[1:-1] % device_id.__dict__) functions = ( ('SENSOR', 'Sensor Device'), ('SDR_REPOSITORY', 'SDR Repository Device'), ('SEL', 'SEL Device'), ('FRU_INVENTORY', 'FRU Inventory Device'), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver'), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator'), ('BRIDGE', 'Bridge'), ('CHASSIS', 'Chassis Device') ) for n, s in functions: if device_id.supports_function(n): print(' %s' % s) if device_id.aux is not None: print('Aux Firmware Rev Info: [%s]' % ( ' '.join('0x%02x' % d for d in device_id.aux))) python-ipmi-0.5.4/pyipmi/000077500000000000000000000000001436677633700153325ustar00rootroot00000000000000python-ipmi-0.5.4/pyipmi/.gitignore000066400000000000000000000000131436677633700173140ustar00rootroot00000000000000version.py python-ipmi-0.5.4/pyipmi/__init__.py000066400000000000000000000171531436677633700174520ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import import time import ast from . import bmc from . import chassis from . import dcmi from . import event from . import fru from . import hpm from . import lan from . import messaging from . import picmg from . import sdr from . import sel from . import sensor from . import msgs from .errors import IpmiTimeoutError, CompletionCodeError, RetryError from .msgs.registry import create_request_by_name from .session import Session from .utils import check_completion_code, is_string try: from version import __version__ except ImportError: __version__ = 'dev' def create_connection(interface): session = Session() session.interface = interface ipmi = Ipmi() ipmi.interface = interface ipmi.session = session ipmi.requester = NullRequester() return ipmi class Requester(object): """The Requester class. This represents an IPMI device which initiates a request/response message exchange. """ def __init__(self, ipmb_address): self.ipmb_address = ipmb_address class NullRequester(object): """The NullRequester class. This requester is used for interfaces which doesn't require a valid requester. """ @property def ipmb_address(self): raise AssertionError('NullRequester does not provide an IPMB address') class Routing(object): """The Target class represents an IPMI target.""" def __init__(self, rq_sa, rs_sa, channel): self.rq_sa = rq_sa self.rs_sa = rs_sa self.channel = channel def __str__(self): s = 'Routing: Rq: %s Rs: %s Ch: %s' \ % (self.rq_sa, self.rs_sa, self.channel) return s class Target(object): """The Target class represents an IPMI target.""" routing = None ipmb_address = None def __init__(self, ipmb_address=None, routing=None): """Initializer for the Target class. `ipmb_address` is the IPMB target address `routing` is the bridging information used to build send message commands. """ if ipmb_address: self.ipmb_address = ipmb_address if routing: self.set_routing(routing) def set_routing_information(self, routing): self.set_routing(routing) def set_routing(self, routing): """Set the path over which a target is reachable. The path is given as a list of tuples in the form (address, bridge_channel). Example #1: access to an ATCA blade in a chassis slave = 0x81, target = 0x82 routing = [(0x81,0x20,0),(0x20,0x82,None)] Example #2: access to an AMC in a uTCA chassis slave = 0x81, target = 0x72 routing = [(0x81,0x20,0),(0x20,0x82,7),(0x20,0x72,None)] uTCA - MCH AMC .-------------------. .--------. | .-----------| | | | ShMC | CM | | MMC | channel=0 | | | channel=7 | | 81 ------------| 0x20 |0x82 0x20 |-------------| 0x72 | | | | | | | | | | | | `-----------| | | `-------------------´ `--------´ `------------´ `---´ `---------------´ Example #3: access to an AMC in a ATCA AMC carrier slave = 0x81, target = 0x72 routing = [(0x81,0x20,0),(0x20,0x8e,7),(0x20,0x80,None)] """ if is_string(routing): # if type(routing) in [unicode, str]: routing = ast.literal_eval(routing) self.routing = [Routing(*route) for route in routing] def __str__(self): string = 'Target: IPMB: 0x%02x\n' % self.ipmb_address if self.routing: for route in self.routing: string += ' %s\n' % route return string class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm, sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan, messaging.Messaging): def __init__(self): self._interface = None self._session = None self._target = None for base in Ipmi.__bases__: base.__init__(self) def is_ipmc_accessible(self): return self.interface.is_ipmc_accessible(self.target) def wait_until_ipmb_is_accessible(self, timeout, interval=0.25): start_time = time.time() while time.time() < start_time + (timeout): try: self.is_ipmc_accessible() except IpmiTimeoutError: time.sleep(interval) self.is_ipmc_accessible() def send_message(self, req, retry=3): req.target = self.target req.requester = self.requester rsp = None while retry > 0: retry -= 1 try: rsp = self.interface.send_and_receive(req) break except CompletionCodeError as e: if e.cc == msgs.constants.CC_NODE_BUSY: continue else: raise RetryError() return rsp def send_message_with_name(self, name, *args, **kwargs): req = create_request_by_name(name) for key, value in kwargs.items(): setattr(req, key, value) rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp def raw_command(self, lun, netfn, raw_bytes): """Send the raw command data and return the raw response. lun: the logical unit number netfn: the network function raw_bytes: the raw message as bytestring Returns the response as bytestring. """ return self.interface.send_and_receive_raw(self.target, lun, netfn, raw_bytes) def _get_interface(self): try: return self._interface except AttributeError: raise RuntimeError('No interface has been set') def _get_session(self): try: return self._session except AttributeError: raise RuntimeError('No IPMI session has been set') def _get_target(self): try: return self._target except AttributeError: raise RuntimeError('No IPMI target has been set') def _set_interface(self, interface): self._interface = interface def _set_session(self, session): self._session = session def _set_target(self, target): self._target = target target = property(_get_target, _set_target) interface = property(_get_interface, _set_interface) session = property(_get_session, _set_session) python-ipmi-0.5.4/pyipmi/bmc.py000066400000000000000000000140421436677633700164460ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .msgs import create_request_by_name from .utils import check_completion_code from .state import State from .fields import VersionField class Bmc(object): def get_device_id(self): return DeviceId(self.send_message_with_name('GetDeviceId')) def cold_reset(self): self.send_message_with_name('ColdReset') def warm_reset(self): self.send_message_with_name('WarmReset') def i2c_write_read(self, bus_type, bus_id, channel, address, count, data=None): req = create_request_by_name('MasterWriteRead') req.bus_id.type = bus_type req.bus_id.id = bus_id req.bus_id.channel = channel req.bus_id.slave_address = address req.read_count = count if data: req.data = data rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.data def i2c_write(self, bus_type, bus_id, channel, address, data): self.i2c_write_read(bus_type, bus_id, channel, address, 0, data) def i2c_read(self, bus_type, bus_id, channel, address, count): return self.i2c_write_read(bus_type, bus_id, channel, address, count, None) def set_watchdog_timer(self, config): req = create_request_by_name('SetWatchdogTimer') req.timer_use.timer_use = config.timer_use req.timer_use.dont_stop = config.dont_stop and 1 or 0 req.timer_use.dont_log = config.dont_log and 1 or 0 req.timer_actions.pre_timeout_interrupt = config.pre_timeout_interrupt req.timer_actions.timeout_action = config.timeout_action req.pre_timeout_interval = config.pre_timeout_interval req.timer_use_expiration_flags = config.timer_use_expiration_flags req.initial_countdown = config.initial_countdown rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_watchdog_timer(self): return Watchdog(self.send_message_with_name('GetWatchdogTimer')) def reset_watchdog_timer(self): self.send_message_with_name('ResetWatchdogTimer') class Watchdog(State): TIMER_USE_OEM = 5 TIMER_USE_SMS_OS = 4 TIMER_USE_OS_LOAD = 3 TIMER_USE_BIOS_POST = 2 TIMER_USE_BIOS_FRB2 = 1 TIMEOUT_ACTION_NO_ACTION = 0 TIMEOUT_ACTION_HARD_RESET = 1 TIMEOUT_ACTION_POWER_DOWN = 2 TIMEOUT_ACTION_POWER_CYCLE = 3 __properties__ = [ # (propery, description) ('timer_use', ''), ('dont_stop', ''), ('is_running', ''), ('dont_log', ''), ('pre_timeout_interrupt', ''), ('timeout_action', ''), ('pre_timeout_interval', ''), ('timer_use_expiration_flags', ''), ('initial_countdown', ''), ('present_countdown', ''), ] def _from_response(self, rsp): self.timer_use = rsp.timer_use.timer_use self.is_running = bool(rsp.timer_use.is_running) self.dont_log = bool(rsp.timer_use.dont_log) self.pre_timeout_interrupt = rsp.timer_actions.pre_timeout_interrupt self.timeout_action = rsp.timer_actions.timeout_action self.pre_timeout_interval = rsp.pre_timeout_interval self.timer_use_expiration_flags = rsp.timer_use_expiration_flags self.initial_countdown = rsp.initial_countdown self.present_countdown = rsp.present_countdown class DeviceId(State): def __str__(self): string = 'Device ID: %d' % self.device_id string += ' revision: %d' % self.revision string += ' available: %d' % self.available string += ' fw version: %s' % (self.fw_revision) string += ' ipmi: %s' % self.ipmi_version string += ' manufacturer: %d' % self.manufacturer_id string += ' product: %d' % self.product_id return string def supports_function(self, name): """Return if a function is supported. `name` is one of 'SENSOR', 'SDR_REPOSITORY', 'SEL', 'FRU_INVENTORY', 'IPMB_EVENT_RECEIVER', 'IPMB_EVENT_GENERATOR', 'BRIDGE', 'CHASSIS'. """ return name.lower() in self.supported_functions def _from_response(self, rsp): self.device_id = rsp.device_id self.revision = rsp.device_revision.device_revision self.provides_sdrs = bool(rsp.device_revision.provides_device_sdrs) self.available = bool(rsp.firmware_revision.device_available) self.fw_revision = VersionField( (rsp.firmware_revision.major, rsp.firmware_revision.minor)) self.ipmi_version = VersionField( (rsp.ipmi_version & 0xf, (rsp.ipmi_version >> 4) & 0xf)) self.manufacturer_id = rsp.manufacturer_id self.product_id = rsp.product_id self.supported_functions = [] functions = ('sensor', 'sdr_repository', 'sel', 'fru_inventory', 'ipmb_event_receiver', 'ipmb_event_generator', 'bridge', 'chassis') for function in functions: if hasattr(rsp.additional_support, function): if getattr(rsp.additional_support, function): self.supported_functions.append(function) self.aux = None if rsp.auxiliary is not None: self.aux = list(rsp.auxiliary) python-ipmi-0.5.4/pyipmi/chassis.py000066400000000000000000000233661436677633700173530ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from enum import Enum from .msgs import create_request_by_name from .utils import check_completion_code, ByteBuffer from .state import State from .msgs.chassis import \ CONTROL_POWER_DOWN, CONTROL_POWER_UP, CONTROL_POWER_CYCLE, \ CONTROL_HARD_RESET, CONTROL_DIAGNOSTIC_INTERRUPT, \ CONTROL_SOFT_SHUTDOWN BOOT_PARAMETER_SET_IN_PROGRESS = 0 BOOT_PARAMETER_SERVICE_PARTITION_SELECTOR = 1 BOOT_PARAMETER_SERVICE_PARTITION_SCAN = 2 BOOT_PARAMETER_BMC_BOOT_FLAG_VALID_BIT_CLEARING = 3 BOOT_PARAMETER_BOOT_INFO_ACKNOWLEDGE = 4 BOOT_PARAMETER_BOOT_FLAGS = 5 BOOT_PARAMETER_BOOT_INITIATOR_INFO = 6 BOOT_PARAMETER_BOOT_INITIATOR_MAILBOX = 7 class BootDevice(str, Enum): NO_OVERRIDE = "no override", PXE = "pxe", DEFAULT_HDD = "default hard drive", DEFAULT_HDD_SAFE = "default hard drive safe mode", DIAGNOSTIC = "diagnostic partition", CD = "cd", BIOS = "bios setup", REMOTE_USB = "remote removable media", PRIMARY_REMOTE = "primary remote media", REMOTE_CD = "remote cd", REMOTE_HDD = "remote hard drive", PRIMARY_USB = "primary removable media (usb)" CONVERT_RAW_TO_BOOT_DEVICE = { 0: BootDevice.NO_OVERRIDE, 1: BootDevice.PXE, 2: BootDevice.DEFAULT_HDD, 3: BootDevice.DEFAULT_HDD_SAFE, 4: BootDevice.DIAGNOSTIC, 5: BootDevice.CD, 6: BootDevice.BIOS, 7: BootDevice.REMOTE_USB, 8: BootDevice.PRIMARY_REMOTE, 9: BootDevice.REMOTE_CD, 11: BootDevice.REMOTE_HDD, 15: BootDevice.PRIMARY_USB } CONVERT_BOOT_DEVICE_TO_RAW = { BootDevice.NO_OVERRIDE: 0b0000, BootDevice.PXE: 0b0001, BootDevice.DEFAULT_HDD: 0b0010, BootDevice.DEFAULT_HDD_SAFE: 0b0011, BootDevice.DIAGNOSTIC: 0b0100, BootDevice.CD: 0b0101, BootDevice.BIOS: 0b0110, BootDevice.REMOTE_USB: 0b0111, BootDevice.PRIMARY_REMOTE: 0b1001, BootDevice.REMOTE_CD: 0b1000, BootDevice.REMOTE_HDD: 0b1011, BootDevice.PRIMARY_USB: 0b1111 } def data_to_boot_mode(data): """ Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data into the string representation of the encoded boot mode. """ boot_mode_raw = (data[0] >> 5) & 1 boot_mode = "legacy" if boot_mode_raw == 0 else "efi" return boot_mode def data_to_boot_persistency(data): """ Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data into the boolean representation of the encoded boot persistency. """ boot_persistent_raw = (data[0] >> 6) & 1 return boot_persistent_raw == 1 def data_to_boot_device(data): """ Convert a `GetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` response data into the string representation of the encoded boot device. """ boot_device_raw = (data[1] >> 2) & 0b1111 return CONVERT_RAW_TO_BOOT_DEVICE[boot_device_raw] def boot_options_to_data(boot_device, boot_mode, boot_persistency): """ Convert a boot mode (string), boot device (string) and boot persistency (bool) into a `SetSystemBootOptions(BOOT_PARAMETER_BOOT_FLAGS)` request data. """ if not isinstance(boot_persistency, bool): raise TypeError(f"Wrong type for boot_persistency argument: {type(boot_persistency)}, expected bool.") # Construct the boot mode byte if boot_mode == "efi": boot_mode_raw = 0b100000 elif boot_mode == "legacy": boot_mode_raw = 0 else: raise ValueError(f"Unknown value for boot_mode argument: {boot_mode}. Possible values are : legacy, efi.") # Construct the boot persistency + boot flags valid bits if boot_persistency: boot_persistent_raw = 0b11000000 else: boot_persistent_raw = 0b10000000 # Construct the boot device byte device_raw = CONVERT_BOOT_DEVICE_TO_RAW.get(boot_device, None) if device_raw is None: raise ValueError(f"Unknown value for boot_device argument: {boot_device}") # Construct the final data bytearray data = ByteBuffer([boot_mode_raw | boot_persistent_raw, device_raw << 2, 0, 0, 0]) return data class Chassis(object): def get_chassis_status(self): return ChassisStatus(self.send_message_with_name('GetChassisStatus')) def chassis_control(self, option): req = create_request_by_name('ChassisControl') req.control.option = option rsp = self.send_message(req) check_completion_code(rsp.completion_code) def chassis_control_power_down(self): self.chassis_control(CONTROL_POWER_DOWN) def chassis_control_power_up(self): self.chassis_control(CONTROL_POWER_UP) def chassis_control_power_cycle(self): self.chassis_control(CONTROL_POWER_CYCLE) def chassis_control_hard_reset(self): self.chassis_control(CONTROL_HARD_RESET) def chassis_control_diagnostic_interrupt(self): self.chassis_control(CONTROL_DIAGNOSTIC_INTERRUPT) def chassis_control_soft_shutdown(self): self.chassis_control(CONTROL_SOFT_SHUTDOWN) def get_system_boot_options(self, parameter_selector=0, set_selector=0, block_selector=0): req = create_request_by_name('GetSystemBootOptions') req.parameter_selector.boot_option_parameter_selector = parameter_selector req.set_selector = set_selector req.block_selector = block_selector rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.data def set_system_boot_options(self, parameter_selector, data, mark_parameter_invalid=0): req = create_request_by_name('SetSystemBootOptions') req.parameter_selector.parameter_validity = mark_parameter_invalid req.parameter_selector.boot_option_parameter_selector = parameter_selector req.data = data rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_boot_mode(self): """ Return a string corresponding to the device boot mode. Possible values are: legacy, efi. """ rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS) return data_to_boot_mode(rsp) def get_boot_persistency(self): """ Return True if the boot configuration is to be applied to every future boot, Fale if it only will applied to the next boot. """ rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS) return data_to_boot_persistency(rsp) def get_boot_device(self): """ Return a string corresponding to the target boot device. Possible values are listed in the `BootDevice` class. """ rsp = self.get_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS) return data_to_boot_device(rsp) def set_boot_options(self, boot_device, boot_mode, boot_persistency): data = boot_options_to_data(boot_device, boot_mode, boot_persistency) self.set_system_boot_options(BOOT_PARAMETER_BOOT_FLAGS, data) class ChassisStatus(State): power_on = None overload = None interlock = None fault = None control_fault = None restore_policy = None id_cmd_state_info_support = None chassis_id_state = None front_panel_button_capabilities = None last_event = [] chassis_state = [] def _from_response(self, rsp): self.power_on = bool(rsp.current_power_state.power_on) self.overload = bool(rsp.current_power_state.power_overload) self.interlock = bool(rsp.current_power_state.interlock) self.fault = bool(rsp.current_power_state.power_fault) self.control_fault = bool(rsp.current_power_state.power_control_fault) self.restore_policy = rsp.current_power_state.power_restore_policy self.id_cmd_state_info_support = \ bool(rsp.misc_chassis_state.id_cmd_state_info_support) # noqa:E127 self.chassis_id_state = rsp.misc_chassis_state.chassis_id_state if rsp.front_panel_button_capabilities is not None: self.front_panel_button_capabilities = \ rsp.front_panel_button_capabilities if rsp.last_power_event.ac_failed: self.last_event.append('ac_failed') if rsp.last_power_event.power_overload: self.last_event.append('overload') if rsp.last_power_event.power_interlock: self.last_event.append('interlock') if rsp.last_power_event.power_fault: self.last_event.append('fault') if rsp.last_power_event.power_is_on_via_ipmi_command: self.last_event.append('power_on_via_ipmi') if rsp.misc_chassis_state.chassis_intrusion_active: self.chassis_state.append('intrusion') if rsp.misc_chassis_state.front_panel_lockout_active: self.chassis_state.append('front_panel_lockout') if rsp.misc_chassis_state.drive_fault: self.chassis_state.append('drive_fault') if rsp.misc_chassis_state.cooling_fault_detected: self.chassis_state.append('cooling_fault') python-ipmi-0.5.4/pyipmi/constants.py000066400000000000000000000024441436677633700177240ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Entity ID assignments ENTITY_ID_CPU = 0x03 ENTITY_ID_BASEBOARD = 0x07 ENTITY_ID_POWER_MODULE = 0x0a ENTITY_ID_COOLING_UNIT = 0x1e ENTITY_ID_AIR_INLET = 0x37 ENTITY_ID_DCMI_AIR_INLET = 0x40 ENTITY_ID_DCMI_CPU = 0x41 ENTITY_ID_DCMI_BASEBOARD = 0x42 ENTITY_ID_PICMG_FRONT_BOARD = 0xa0 ENTITY_ID_PICMG_REAR_TRANSITION_MODULE = 0xc0 ENTITY_ID_PICMG_ADVANCED_MC = 0xc1 ENTITY_ID_PICMG_MICROTCA_CARRIER_HUB = 0xc2 ENTITY_ID_PICMG_SHELF_MANAGEMENT_CONTROLLER = 0xf0 ENTITY_ID_PICMG_FILTRATION_UNIT = 0xf1 ENTITY_ID_PICMG_SHELF_FRU_INFORMATION = 0xf2 python-ipmi-0.5.4/pyipmi/dcmi.py000066400000000000000000000044671436677633700166330ustar00rootroot00000000000000# Copyright (c) 2018 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .constants import (ENTITY_ID_DCMI_AIR_INLET, ENTITY_ID_DCMI_CPU, ENTITY_ID_DCMI_BASEBOARD) PARAM_SUPPORTED_DCMI_CAPABILITIES = 1 PARAM_MANDATORY_PLATFORM_ATTRIBUTES = 2 PARAM_OPTIONAL_PLATFORM_ATTRIBUTES = 3 PARAM_MANAGEABILITY_ACCESS_ATTRIBUTES = 4 PARAM_ENHANCED_SYSTEM_POWER_STATISTICS_ATTRIBUTES = 5 class Dcmi(object): def get_dcmi_capabilities(self, selector): rsp = self.send_message_with_name('GetDcmiCapabilities', parameter_selector=selector) return rsp def get_power_reading(self, mode, attributes=0): rsp = self.send_message_with_name('GetPowerReading', mode=mode, attributes=attributes) return rsp def get_dcmi_sensor_record_ids(self): record_ids = list() DCMI_ENTITIES = (ENTITY_ID_DCMI_AIR_INLET, ENTITY_ID_DCMI_CPU, ENTITY_ID_DCMI_BASEBOARD) for entity_id in DCMI_ENTITIES: rsp = self.send_message_with_name('GetDcmiSensorInfo', sensor_type=1, entity_id=entity_id, entity_instance=0, entity_instance_start=0) # convert the returned raw data in a list of SDR record IDs ids = [msb << 8 | lsb for (lsb, msb) in zip(rsp.record_ids, rsp.record_ids[1:])[::2]] record_ids.extend(ids) return record_ids python-ipmi-0.5.4/pyipmi/emulation.py000066400000000000000000000261431436677633700177070ustar00rootroot00000000000000import argparse import logging import os import random import socket import threading import yaml from array import array from collections import OrderedDict import pyipmi from pyipmi.logger import log from pyipmi.interfaces import rmcp from pyipmi.interfaces import ipmb from pyipmi.msgs import (create_message, decode_message, encode_message, create_response_message, create_request_by_name) from pyipmi.msgs import constants from pyipmi.session import Session UDP_IP = "127.0.0.1" UDP_PORT = 1623 sdr_list = OrderedDict() handler_registry = {} def register_message_handler(msg_name): def reg(fn): msg_type = type(create_request_by_name(msg_name)) handler_registry[msg_type] = fn return fn return reg @register_message_handler("GetChannelAuthenticationCapabilities") def handle_channel_auth_caps(context, req): rsp = create_response_message(req) rsp.support.straight = 1 rsp.status.anonymous_login_enabled = 1 return rsp @register_message_handler("GetSessionChallenge") def handle_get_session_challenge(context, req): rsp = create_response_message(req) rsp.temporary_session_id = random.randrange(1, 0xffffffff) return rsp @register_message_handler("ActivateSession") def handle_activate_session(context, req): session = context.session rsp = create_response_message(req) rsp.session_id = random.randrange(1, 0xffffffff) rsp.authentication.type = req.authentication.type rsp.privilege_level.maximum_allowed = req.privilege_level.maximum_requested session.session_id = rsp.session_id session.set_auth_type_user('admin', 'admin') session.auth_type = Session.AUTH_TYPE_PASSWORD return rsp @register_message_handler("CloseSession") def handle_close_session(context, req): rsp = create_response_message(req) return rsp @register_message_handler("SetSessionPrivilegeLevel") def handle_set_session_priv_level(context, req): rsp = create_response_message(req) return rsp @register_message_handler("GetDeviceId") def handle_get_device_id(context, req): rsp = create_response_message(req) return rsp @register_message_handler("GetFruInventoryAreaInfo") def handle_fru_inventory_are_info(context, req): rsp = create_response_message(req) cfg = context.config try: fru_filename = cfg['fru'][req.fru_id] except KeyError: log().warning('cannot find frufile for fru_id={} in config'.format(req.fru_id)) rsp.completion_code = constants.CC_PARAM_OUT_OF_RANGE return rsp except TypeError: log().warning('cannot find frufile for fru_id={} in config'.format(req.fru_id)) rsp.completion_code = constants.CC_REQ_DATA_NOT_PRESENT return rsp try: statinfo = os.stat(fru_filename) rsp.area_size = statinfo.st_size except FileNotFoundError: log().warning('cannot open file={} for fru_id={}'.format(fru_filename, req.fru_id)) rsp.completion_code = constants.CC_PARAM_OUT_OF_RANGE return rsp return rsp @register_message_handler("ReadFruData") def handle_fru_read(context, req): rsp = create_response_message(req) cfg = context.config try: fru_filename = cfg['fru'][req.fru_id] except KeyError: rsp.completion_code = constants.CC_PARAM_OUT_OF_RANGE log().debug('cannot find file for fru_id={} in config'.format(req.fru_id)) return rsp try: with open(fru_filename, 'rb') as fru: fru.seek(req.offset) d = fru.read(req.count) rsp.count = len(d) rsp.data = d except FileNotFoundError: log().debug('cannot open file={} for fru_id={}'.format(fru_filename, req.fru_id)) rsp.completion_code = constants.CC_PARAM_OUT_OF_RANGE return rsp return rsp @register_message_handler("GetSdrRepositoryInfo") def handle_sdr_repository_info(context, req): rsp = create_response_message(req) rsp.count = 0 return rsp @register_message_handler("ReserveSdrRepository") def handle_reserve_sdr_repositry(context, req): rsp = create_response_message(req) return rsp @register_message_handler("ClearSdrRepository") def handle_clear_sdr_repositry(context, req): rsp = create_response_message(req) rsp.status.erase_in_progress = constants.REPOSITORY_ERASURE_COMPLETED return rsp @register_message_handler("GetSdr") def handle_get_sdr(context, req): rsp = create_response_message(req) if len(sdr_list) == 0: log().warning('no SDR present') rsp.completion_code = constants.CC_REQ_DATA_NOT_PRESENT return rsp next_index = list(sdr_list.keys()).index(req.record_id) + 1 try: next_record_id = list(sdr_list)[next_index] except IndexError: next_record_id = 0xffff rsp.next_record_id = next_record_id sdr = sdr_list[req.record_id] rsp.record_data = sdr.data[req.offset:req.offset+req.bytes_to_read] return rsp @register_message_handler("GetDeviceSdrInfo") def handle_device_sdr_info(context, req): rsp = create_response_message(req) rsp.number_of_sensors = 0 return rsp @register_message_handler("ReserveDeviceSdrRepository") def handle_reserve_device_sdr_repository(context, req): rsp = create_response_message(req) return rsp @register_message_handler("SendMessage") def handle_send_message(context, req): rsp = create_response_message(req) rsp.completion_code = constants.CC_PARAM_OUT_OF_RANGE return rsp def handle_ipmi_request_msg(context, req): try: fct = handler_registry[type(req)] except KeyError: rsp = create_response_message(req) log().warning('no handler for: {}'.format(type(req))) rsp.completion_code = constants.CC_INV_CMD return rsp rsp = fct(context, req) return rsp def handle_rmcp_asf_msg(context, sdu): asf = rmcp.AsfMsg() asf.unpack(sdu) # t = rmcp.AsfMsg().from_data(sdu) if asf.asf_type == rmcp.AsfMsg.ASF_TYPE_PRESENCE_PING: log().debug(f'ASF RX: ping: {asf}') pong = rmcp.AsfPong() pdu = pong.pack() log().debug(f'ASF TX: pong: {asf}') return pdu def handle_rmcp_ipmi_msg(context, sdu): def _get_group_id(ipmi_sdu): group_id = None if req_header.netfn == constants.NETFN_GROUP_EXTENSION: group_id = ipmi_sdu[6] return group_id def _create_invalid_response(ipmi_sdu): req_header = ipmb.IpmbHeaderReq(data=ipmi_sdu) group_id = _get_group_id(ipmi_sdu) log().warning('Cant create message: netfn 0x{:x} cmd: 0x{:x} group: {}'.format(req_header.netfn, req_header.cmdid, group_id)) log().debug('IPMI RX: {:s}'.format( ' '.join('%02x' % b for b in array('B', ipmi_sdu)))) # bytes are immutable ... so convert to change a = bytearray(ipmi_sdu) # set completion code . invalid command a[6] = constants.CC_INV_CMD # netfn + 1 a[1] = a[1] | 0x4 ipmi_sdu = bytes(a) # rmcp ipmi rsp msg ipmi_tx = rmcp.IpmiMsg(context.session) pdu = ipmi_tx.pack(ipmi_sdu) return pdu session = context.session session.sequence_number += 1 if session.sequence_number > 255: session.sequence_number = 0 # rmcp ipmi req msg ipmi_rx = rmcp.IpmiMsg() ipmi_sdu = ipmi_rx.unpack(sdu) req_header = ipmb.IpmbHeaderReq(data=ipmi_sdu) group_id = _get_group_id(ipmi_sdu) try: req = create_message(req_header.netfn, req_header.cmdid, group_id) except KeyError: return _create_invalid_response(ipmi_sdu) log().debug('IPMI RX: {}: {:s}'.format(req, ' '.join('%02x' % b for b in array('B', ipmi_sdu)))) decode_message(req, ipmi_sdu[6:-1]) rsp = handle_ipmi_request_msg(context, req) data = encode_message(rsp) rsp_header = ipmb.IpmbHeaderRsp() rsp_header.from_req_header(req_header) tx_data = ipmb.encode_ipmb_msg(rsp_header, data) log().debug('IPMI TX: {}: {:s}'.format(rsp, ' '.join('%02x' % b for b in array('B', tx_data)))) # rmcp ipmi rsp msg ipmi_tx = rmcp.IpmiMsg(context.session) pdu = ipmi_tx.pack(tx_data) if type(req) == pyipmi.msgs.device_messaging.CloseSessionReq: session.session_id = None session._auth_type = Session.AUTH_TYPE_NONE session._auth_username = None session._auth_password = None context.state = ConnectionContext.STATE_CLOSED return pdu def load_sdr_dump(dump_file): with open(dump_file, 'rb') as f: while True: h = f.read(5) if not h: break t = pyipmi.sdr.SdrCommon(h) b = f.read(t.length) sdr = pyipmi.sdr.SdrCommon().from_data(h + b) sdr_list[sdr.id] = sdr class ConnectionContext(): STATE_IDLE = 0 STATE_ACTIVE = 1 STATE_CLOSED = 2 state = STATE_IDLE def __init__(self, config, sock, addr): self.config = config self.sock = sock self.addr = addr self.session = Session() def handle_thread(context, pdu): msg = rmcp.RmcpMsg() sdu = msg.unpack(pdu) try: handler = { rmcp.RMCP_CLASS_ASF: handle_rmcp_asf_msg, rmcp.RMCP_CLASS_IPMI: handle_rmcp_ipmi_msg, }[msg.class_of_msg] tx_data = handler(context, sdu) rmcp_msg = rmcp.RmcpMsg(msg.class_of_msg) pdu = rmcp_msg.pack(tx_data, context.session.sequence_number) context.sock.sendto(pdu, context.addr) except IndexError: print('unknown class_of_msg {}'.format(msg.class_of_msg)) def main(args=None): parser = argparse.ArgumentParser(description="IPMI server emulation.") parser.add_argument("-p", "--port", type=int, dest="port", help="RMCP port", default=623) parser.add_argument("-c", "--config", type=str, dest="config", help="Config file") parser.add_argument( "-v", action="store_true", dest="verbose", help="be more verbose" ) args = parser.parse_args(args) handler = logging.StreamHandler() if args.verbose: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.INFO) pyipmi.logger.add_log_handler(handler) pyipmi.logger.set_log_level(logging.DEBUG) config = None if args.config: with open(args.config, 'r') as stream: config = yaml.safe_load(stream) if 'sdr' in config: load_sdr_dump(config['sdr']) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, args.port)) connections = {} while True: pdu, addr = sock.recvfrom(1024) # buffer size is 1024 bytes # create new connection if addr not in connections: connections[addr] = ConnectionContext(config, sock, addr) # remove for closed connections from dict connections = { key: value for key, value in connections.items() if value.state != value.STATE_CLOSED } thread = threading.Thread(target=handle_thread, args=(connections[addr], pdu)) thread.daemon = True thread.start() if __name__ == '__main__': main() python-ipmi-0.5.4/pyipmi/errors.py000066400000000000000000000041341436677633700172220ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .msgs.constants import COMPLETION_CODE_DESCR class DecodingError(Exception): """Error on message decoding.""" pass class EncodingError(Exception): """Error on message encoding.""" pass class IpmiTimeoutError(Exception): """Timeout occurred.""" pass class CompletionCodeError(Exception): """IPMI completion code not OK.""" def __init__(self, cc, cmdid=None): self.cc = cc self.cc_desc = self.find_cc_desc(cc) def __str__(self): return "%s cc=0x%02x desc=%s" \ % (self.__class__.__name__, self.cc, self.cc_desc) @staticmethod def find_cc_desc(error_cc): for cc in COMPLETION_CODE_DESCR: if error_cc == cc[0]: return cc[1] return "Unknown error description" class NotSupportedError(Exception): """Not supported yet.""" pass class DescriptionError(Exception): """Message description incorrect.""" pass class RetryError(Exception): """Maxium number of retries exceeded.""" pass class DataNotFound(Exception): """Requested data not found.""" pass class HpmError(Exception): """HPM.1 error.""" pass class IpmiConnectionError(Exception): """Connection error.""" def __init__(self, msg=None): self.msg = msg def __str__(self): return "{}".format(self.msg) python-ipmi-0.5.4/pyipmi/event.py000066400000000000000000000030011436677633700170170ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .utils import check_completion_code from .msgs import create_request_by_name EVENT_ASSERTION = 0 EVENT_DEASSERTION = 1 class Event(object): def set_event_receiver(self, ipmb_address, lun): req = create_request_by_name('SetEventReceiver') req.event_receiver.ipmb_i2c_slave_address = ipmb_address req.event_receiver.lun = lun rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_event_receiver(self): req = create_request_by_name('GetEventReceiver') rsp = self.send_message(req) check_completion_code(rsp.completion_code) ipmb_address = rsp.event_receiver.ipmb_i2c_slave_address lun = rsp.event_receiver.lun return (ipmb_address, lun) python-ipmi-0.5.4/pyipmi/fields.py000066400000000000000000000061161436677633700171560ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import array from .errors import DecodingError from .utils import py3_array_tobytes class VersionField(object): """This class represent the Version fields defines by IPMI. Introduced with HPM the version field can hold additional auxiliary bytes. """ VERSION_FIELD_LEN = 2 VERSION_WITH_AUX_FIELD_LEN = 6 def __init__(self, data=None): self.major = None self.minor = None if data: self._from_data(data) def _from_data(self, data): if isinstance(data, str): data = [ord(c) for c in data] data = array.array('B', data) self.version = self._decode_data(data[0:2]) if len(data) == self.VERSION_WITH_AUX_FIELD_LEN: self.auxiliary = data[2:6] def __str__(self): return self.version_to_string() def _decode_data(self, data): """`data` is array.array.""" self.major = data[0] if data[1] == 0xff: self.minor = data[1] elif data[1] <= 0x99: self.minor = int(py3_array_tobytes(data[1:2]).decode('bcd+')) else: raise DecodingError() def version_to_string(self): return ''.join("%s.%s" % (self.major, self.minor)) class TypeLengthString(object): """ This is the TYPE/LENGTH BYTE FORMAT field represenation according the Platform Management FRU Information Storage Definition v1.0. In addition the difference to the 'FRU Information Storage Definition' to the variant used in Type/Length for the Device ID String used in the SDR. """ TYPE_FRU_BINARY = 0 TYPE_SDR_UNICODE = 0 TYPE_BCD_PLUS = 1 TYPE_6BIT_ASCII = 2 TYPE_ASCII_OR_UTF16 = 3 def __init__(self, data=None, offset=0, force_lang_eng=False, sdr=False): if data: self._from_data(data, offset, force_lang_eng) def __str__(self): if self.field_type is self.TYPE_FRU_BINARY: return ' '.join('%02x' % b for b in self.raw) else: return self.string.replace('\x00', '') def _from_data(self, data, offset=0, force_lang_eng=False): self.offset = offset self.field_type = data[offset] >> 6 & 0x3 self.length = data[offset] & 0x3f self.raw = data[offset+1:offset+1+self.length] chr_data = ''.join([chr(c) for c in self.raw]) if self.field_type == self.TYPE_BCD_PLUS: self.string = chr_data.decode('bcd+') elif self.field_type == self.TYPE_6BIT_ASCII: self.string = chr_data.decode('6bitascii') else: self.string = chr_data class FruTypeLengthString(TypeLengthString): def __init__(self, data=None, offset=0, force_lang_eng=False): super(FruTypeLengthString, self).__init__(data, offset, force_lang_eng, sdr=False) class SdrTypeLengthString(TypeLengthString): def __init__(self, data=None, offset=0, force_lang_eng=False): super(SdrTypeLengthString, self).__init__(data, sdr=True) python-ipmi-0.5.4/pyipmi/fru.py000066400000000000000000000402061436677633700165020ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import array import codecs import datetime import os from .errors import DecodingError, CompletionCodeError from .msgs import constants from .utils import bcd_search, chunks, py3_array_tobytes from .fields import FruTypeLengthString codecs.register(bcd_search) class Fru(object): def __init__(self): self.write_length = 16 def get_fru_inventory_area_info(self, fru_id=0): rsp = self.send_message_with_name('GetFruInventoryAreaInfo', fru_id=fru_id) return rsp.area_size def write_fru_data(self, data, offset=0, fru_id=0): for chunk in chunks(data, self.write_length): write_rsp = self.send_message_with_name('WriteFruData', fru_id=fru_id, offset=offset, data=chunk) # check if device wrote the same number of bytes sent if write_rsp.count_written != len(chunk): raise Exception('sent {:} bytes but device wrote {:} bytes' .format(len(chunk), write_rsp.count_written)) offset += len(chunk) def read_fru_data(self, offset=None, count=None, fru_id=0): req_size = 32 data = array.array('B') # first check for maximum area size if offset is None: area_size = self.get_fru_inventory_area_info(fru_id) off = 0 else: area_size = offset + count off = offset while off < area_size: if (off + req_size) > area_size: req_size = area_size - off try: rsp = self.send_message_with_name('ReadFruData', fru_id=fru_id, offset=off, count=req_size) except CompletionCodeError as ex: if ex.cc in (constants.CC_CANT_RET_NUM_REQ_BYTES, constants.CC_REQ_DATA_FIELD_EXCEED, constants.CC_PARAM_OUT_OF_RANGE): req_size -= 2 if req_size <= 0: raise continue else: raise data.extend(rsp.data) off += rsp.count return py3_array_tobytes(data) def read_fru_data_full(self, fru_id=0): return self.read_fru_data(fru_id=fru_id) def get_fru_inventory_header(self, fru_id=0): data = self.read_fru_data(offset=0, count=8, fru_id=fru_id) return InventoryCommonHeader(data) def _read_fru_area(self, offset, fru_id=0): # read the area header data = self.read_fru_data(offset=offset, count=5, fru_id=fru_id) # get the whole area data count = data[1] * 8 return self.read_fru_data(offset=offset, count=count, fru_id=fru_id) def get_fru_chassis_area(self, fru_id=0): header = self.get_fru_inventory_header(fru_id=fru_id) data = self._read_fru_area(offset=header.chassis_info_area_offset, fru_id=fru_id) return InventoryChassisInfoArea(data) def get_fru_board_area(self, fru_id=0): header = self.get_fru_inventory_header(fru_id=fru_id) data = self._read_fru_area(offset=header.board_info_area_offset, fru_id=fru_id) return InventoryBoardInfoArea(data) def get_fru_product_area(self, fru_id=0): header = self.get_fru_inventory_header(fru_id=fru_id) data = self._read_fru_area(offset=header.product_info_area_offset, fru_id=fru_id) return InventoryProductInfoArea(data) def get_fru_multirecord_area(self, fru_id=0): header = self.get_fru_inventory_header(fru_id=fru_id) # we have to determine the length of the area first offset = header.multirecord_area_offset count = 0 while True: # read the header data = self.read_fru_data(offset=offset, count=5) end_of_list = bool(data[1] & 0x80) length = data[2] count += length + 5 offset += length + 5 if end_of_list: break # now read the full area offset = header.multirecord_area_offset data = self.read_fru_data(offset=offset, count=count) return InventoryMultiRecordArea(data) def get_fru_inventory(self, fru_id=0): """ Get the full parsed FRU inventory data. """ fru = FruInventory() header = self.get_fru_inventory_header(fru_id=fru_id) if header.chassis_info_area_offset: fru.chassis_info_area = self.get_fru_chassis_area(fru_id=fru_id) if header.board_info_area_offset: fru.board_info_area = self.get_fru_board_area(fru_id=fru_id) if header.product_info_area_offset: fru.product_info_area = self.get_fru_product_area(fru_id=fru_id) if header.multirecord_area_offset: fru.multirecord_area = self.get_fru_multirecord_area(fru_id=fru_id) return fru def get_fru_inventory_from_file(filename): try: file = open(filename, "rb") except IOError: print('Error open file "%s"' % filename) ################################ # get file size file_size = os.stat(filename).st_size file_data = file.read(file_size) data = array.array('B', file_data) file.close() return FruInventory(data) CUSTOM_FIELD_END = 0xc1 def _decode_custom_fields(data): offset = 0 fields = [] while data[offset] != CUSTOM_FIELD_END: field = FruTypeLengthString(data, offset) fields.append(field) offset += field.length + 1 return fields class FruData(object): def __init__(self, data=None): if data: if isinstance(data, str): data = [ord(c) for c in data] self.data = data if hasattr(self, '_from_data'): self._from_data(data) class InventoryCommonHeader(FruData): def _from_data(self, data): if len(data) != 8: raise DecodingError('InventoryCommonHeader length != 8') self.format_version = data[0] & 0x0f self.internal_use_area_offset = data[1] * 8 or None self.chassis_info_area_offset = data[2] * 8 or None self.board_info_area_offset = data[3] * 8 or None self.product_info_area_offset = data[4] * 8 or None self.multirecord_area_offset = data[5] * 8 or None if sum(data) % 256 != 0: raise DecodingError('InventoryCommonHeader checksum failed') class CommonInfoArea(FruData): def _from_data(self, data): self.format_version = data[0] & 0x0f if self.format_version != 1: raise DecodingError('unsupported format version (%d)' % self.format_version) self.length = data[1] * 8 if sum(data[:self.length]) % 256 != 0: raise DecodingError('checksum failed') class InventoryChassisInfoArea(CommonInfoArea): TYPE_OTHER = 1 TYPE_UNKNOWN = 2 TYPE_DESKTOP = 3 TYPE_LOW_PROFILE_DESKTOP = 4 TYPE_PIZZA_BOX = 5 TYPE_MINI_TOWER = 6 TYPE_TOWER = 7 TYPE_PORTABLE = 8 TYPE_LAPTOP = 9 TYPE_NOTEBOOK = 10 TYPE_HAND_HELD = 11 TYPE_DOCKING_STATION = 12 TYPE_ALL_IN_ONE = 13 TYPE_SUB_NOTEBOOK = 14 TYPE_SPACE_SAVING = 15 TYPE_LUNCH_BOX = 16 TYPE_MAIN_SERVER_CHASSIS = 17 TYPE_EXPANSION_CHASSIS = 18 TYPE_SUB_CHASSIS = 19 TYPE_BUS_EXPANSION_CHASSIS = 20 TYPE_PERIPHERAL_CHASSIS = 21 TYPE_RAID_CHASSIS = 22 TYPE_RACK_MOUNT_CHASSIS = 23 def _from_data(self, data): CommonInfoArea._from_data(self, data) self.type = data[2] offset = 3 self.part_number = FruTypeLengthString(data, offset) offset += self.part_number.length + 1 self.serial_number = FruTypeLengthString(data, offset, True) offset += self.serial_number.length + 1 self.custom_chassis_info = _decode_custom_fields(data[offset:]) class InventoryBoardInfoArea(CommonInfoArea): def _from_data(self, data): CommonInfoArea._from_data(self, data) self.language_code = data[2] minutes = data[5] << 16 | data[4] << 8 | data[3] self.mfg_date = (datetime.datetime(1996, 1, 1) + datetime.timedelta(minutes=minutes)) offset = 6 self.manufacturer = FruTypeLengthString(data, offset) offset += self.manufacturer.length + 1 self.product_name = FruTypeLengthString(data, offset) offset += self.product_name.length + 1 self.serial_number = FruTypeLengthString(data, offset, True) offset += self.serial_number.length + 1 self.part_number = FruTypeLengthString(data, offset) offset += self.part_number.length + 1 self.fru_file_id = FruTypeLengthString(data, offset, True) offset += self.fru_file_id.length + 1 self.custom_mfg_info = _decode_custom_fields(data[offset:]) class InventoryProductInfoArea(CommonInfoArea): def _from_data(self, data): CommonInfoArea._from_data(self, data) self.language_code = data[2] offset = 3 self.manufacturer = FruTypeLengthString(data, offset) offset += self.manufacturer.length + 1 self.name = FruTypeLengthString(data, offset) offset += self.name.length + 1 self.part_number = FruTypeLengthString(data, offset) offset += self.part_number.length + 1 self.version = FruTypeLengthString(data, offset) offset += self.version.length + 1 self.serial_number = FruTypeLengthString(data, offset, True) offset += self.serial_number.length + 1 self.asset_tag = FruTypeLengthString(data, offset) offset += self.asset_tag.length + 1 self.fru_file_id = FruTypeLengthString(data, offset, True) offset += self.fru_file_id.length + 1 self.custom_mfg_info = list() self.custom_mfg_info = _decode_custom_fields(data[offset:]) class FruDataMultiRecord(FruData): TYPE_POWER_SUPPLY_INFORMATION = 0 TYPE_DC_OUTPUT = 1 TYPE_DC_LOAD = 2 TYPE_MANAGEMENT_ACCESS_RECORD = 3 TYPE_BASE_COMPATIBILITY_RECORD = 4 TYPE_EXTENDED_COMPATIBILITY_RECORD = 5 TYPE_OEM = list(range(0x0c, 0x100)) TYPE_OEM_PICMG = 0xc0 def __str__(self): return '%02x: %s' % (self.record_type_id, ' '.join('%02x' % b for b in self.raw)) def _from_data(self, data): if len(data) < 5: raise DecodingError('data too short') self.record_type_id = data[0] self.format_version = data[1] & 0x0f self.end_of_list = bool(data[1] & 0x80) self.length = data[2] if sum(data[:5]) % 256 != 0: raise DecodingError('FruDataMultiRecord header checksum failed') self.raw = data[5:5+self.length] if (sum(self.raw) + data[3]) % 256 != 0: raise DecodingError('FruDataMultiRecord record checksum failed') @staticmethod def create_from_record_id(data): if data[0] == FruDataMultiRecord.TYPE_OEM_PICMG: return FruPicmgRecord.create_from_record_id(data) else: return FruDataUnknown(data) class FruDataUnknown(FruDataMultiRecord): """This class is used to indicate undecoded picmg record.""" pass class FruPicmgRecord(FruDataMultiRecord): PICMG_RECORD_ID_BACKPLANE_PTP_CONNECTIVITY = 0x04 PICMG_RECORD_ID_ADDRESS_TABLE = 0x10 PICMG_RECORD_ID_SHELF_POWER_DISTRIBUTION = 0x11 PICMG_RECORD_ID_SHMC_ACTIVATION_MANAGEMENT = 0x12 PICMG_RECORD_ID_SHMC_IP_CONNECTION = 0x13 PICMG_RECORD_ID_BOARD_PTP_CONNECTIVITY = 0x14 PICMG_RECORD_ID_RADIAL_IPMB0_LINK_MAPPING = 0x15 PICMG_RECORD_ID_MODULE_CURRENT_REQUIREMENTS = 0x16 PICMG_RECORD_ID_CARRIER_ACTIVATION_MANAGEMENT = 0x17 PICMG_RECORD_ID_CARRIER_PTP_CONNECTIVITY = 0x18 PICMG_RECORD_ID_AMC_PTP_CONNECTIVITY = 0x19 PICMG_RECORD_ID_CARRIER_INFORMATION = 0x1a PICMG_RECORD_ID_MTCA_FRU_INFORMATION_PARTITION = 0x20 PICMG_RECORD_ID_MTCA_CARRIER_MANAGER_IP_LINK = 0x21 PICMG_RECORD_ID_MTCA_CARRIER_INFORMATION = 0x22 PICMG_RECORD_ID_MTCA_SHELF_INFORMATION = 0x23 PICMG_RECORD_ID_MTCA_SHELF_MANAGER_IP_LINK = 0x24 PICMG_RECORD_ID_MTCA_CARRIER_POWER_POLICY = 0x25 PICMG_RECORD_ID_MTCA_CARRIER_ACTIVATION_AND_POWER = 0x26 PICMG_RECORD_ID_MTCA_POWER_MODULE_CAPABILITY = 0x27 PICMG_RECORD_ID_MTCA_FAN_GEOGRAPHY = 0x28 PICMG_RECORD_ID_OEM_MODULE_DESCRIPTION = 0x29 PICMG_RECORD_ID_CARRIER_CLOCK_PTP_CONNECTIVITY = 0x2C PICMG_RECORD_ID_CLOCK_CONFIGURATION = 0x2d PICMG_RECORD_ID_ZONE_3_INTERFACE_COMPATIBILITY = 0x30 PICMG_RECORD_ID_CARRIER_BUSED_CONNECTIVITY = 0x31 PICMG_RECORD_ID_ZONE_3_INTERFACE_DOCUMENTATION = 0x32 def __init__(self, data): FruDataMultiRecord.__init__(self, data) @staticmethod def create_from_record_id(data): picmg_record = FruPicmgRecord(data) if picmg_record.picmg_record_type_id ==\ FruPicmgRecord.PICMG_RECORD_ID_MTCA_POWER_MODULE_CAPABILITY: return FruPicmgPowerModuleCapabilityRecord(data) return FruPicmgRecord(data) def _from_data(self, data): if len(data) < 10: raise DecodingError('data too short') data = array.array('B', data) FruDataMultiRecord._from_data(self, data) self.manufacturer_id = \ data[5] | data[6] << 8 | data[7] << 16 self.picmg_record_type_id = data[8] self.format_version = data[9] class FruPicmgPowerModuleCapabilityRecord(FruPicmgRecord): def _from_data(self, data): if len(data) < 12: raise DecodingError('data too short') FruPicmgRecord._from_data(self, data) maximum_current_output = data[10] | data[11] << 8 self.maximum_current_output = float(maximum_current_output/10) class InventoryMultiRecordArea(object): def __init__(self, data): if data: self._from_data(data) def _from_data(self, data): self.records = list() offset = 0 while True: record = FruDataMultiRecord.create_from_record_id(data[offset:]) self.records.append(record) offset += record.length + 5 if record.end_of_list: break class FruInventory(object): def __init__(self, data=None): self.chassis_info_area = None self.board_info_area = None self.product_info_area = None self.multirecord_area = None if data: self._from_data(data) def _from_data(self, data): self.raw = data self.common_header = InventoryCommonHeader(data[:8]) if self.common_header.chassis_info_area_offset: self.chassis_info_area = InventoryChassisInfoArea( data[self.common_header.chassis_info_area_offset:]) if self.common_header.board_info_area_offset: self.board_info_area = InventoryBoardInfoArea( data[self.common_header.board_info_area_offset:]) if self.common_header.product_info_area_offset: self.product_info_area = InventoryProductInfoArea( data[self.common_header.product_info_area_offset:]) if self.common_header.multirecord_area_offset: self.multirecord_area = InventoryMultiRecordArea( data[self.common_header.multirecord_area_offset:]) python-ipmi-0.5.4/pyipmi/helper.py000066400000000000000000000111031436677633700171570ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import time from .errors import CompletionCodeError, RetryError from .utils import check_completion_code, ByteBuffer from .msgs import constants def get_sdr_chunk_helper(send_fn, req, reserve_fn, retry=5): while True: retry -= 1 if retry == 0: raise RetryError() rsp = send_fn(req) if rsp.completion_code == constants.CC_OK: break elif rsp.completion_code == constants.CC_RES_CANCELED: req.reservation_id = reserve_fn() time.sleep(0.1) continue elif rsp.completion_code == constants.CC_TIMEOUT: time.sleep(0.1) continue elif rsp.completion_code == constants.CC_RESP_COULD_NOT_BE_PRV: time.sleep(0.1 * retry) continue else: check_completion_code(rsp.completion_code) return rsp def get_sdr_data_helper(reserve_fn, get_fn, record_id, reservation_id=None): """Helper function to retrieve the sdr data. A specified helper function is used to retrieve the chunks. This can be used for SDRs from the Sensor Device or form the SDR repository. """ if reservation_id is None: reservation_id = reserve_fn() (next_id, data) = get_fn(reservation_id, record_id, 0, 5) header = ByteBuffer(data) record_id = header.pop_unsigned_int(2) record_version = header.pop_unsigned_int(1) # noqa:F841 record_type = header.pop_unsigned_int(1) # noqa:F841 record_payload_length = header.pop_unsigned_int(1) record_length = record_payload_length + 5 record_data = ByteBuffer(data) offset = len(record_data) max_req_len = 20 retry = 20 # now get the other record data while True: retry -= 1 if retry == 0: raise RetryError() length = max_req_len if (offset + length) > record_length: length = record_length - offset try: (next_id, data) = get_fn(reservation_id, record_id, offset, length) except CompletionCodeError as e: if e.cc == constants.CC_CANT_RET_NUM_REQ_BYTES: # reduce max lenght max_req_len -= 4 if max_req_len <= 0: retry = 0 else: raise CompletionCodeError(e.cc) record_data.extend(data[:]) offset = len(record_data) if len(record_data) >= record_length: break return (next_id, record_data) def _clear_repository(reserve_fn, clear_fn, ctrl, retry, reservation): while True: retry -= 1 if retry <= 0: raise RetryError() try: in_progress = clear_fn(ctrl, reservation) except CompletionCodeError as e: if e.cc == constants.CC_RES_CANCELED: time.sleep(0.2) reservation = reserve_fn() continue else: check_completion_code(e.cc) if in_progress == constants.REPOSITORY_ERASURE_IN_PROGRESS: time.sleep(0.5) continue break return reservation def clear_repository_helper(reserve_fn, clear_fn, retry=5, reservation=None): """Helper function to start repository erasure and wait until finish. This helper is used by clear_sel and clear_sdr_repository. """ if reservation is None: reservation = reserve_fn() # start erasure reservation = _clear_repository(reserve_fn, clear_fn, constants.REPOSITORY_INITIATE_ERASE, retry, reservation) # give some time to clear time.sleep(0.5) # wait until finish reservation = _clear_repository(reserve_fn, clear_fn, constants.REPOSITORY_GET_ERASE_STATUS, retry, reservation) python-ipmi-0.5.4/pyipmi/hpm.py000066400000000000000000000626561436677633700165070ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function import os import codecs import struct import collections import hashlib import time from array import array from .errors import CompletionCodeError, HpmError, IpmiTimeoutError from .msgs import create_request_by_name from .msgs import constants from .utils import check_completion_code, bcd_search, chunks from .utils import py3dec_unic_bytes_fix, py3_array_tobytes from .state import State from .fields import VersionField PROPERTY_GENERAL_PROPERTIES = 0 PROPERTY_CURRENT_VERSION = 1 PROPERTY_DESCRIPTION_STRING = 2 PROPERTY_ROLLBACK_VERSION = 3 PROPERTY_DEFERRED_VERSION = 4 PROPERTY_OEM = list(range(192, 255)) ACTION_BACKUP_COMPONENT = 0x00 ACTION_PREPARE_COMPONENT = 0x01 ACTION_UPLOAD_FOR_UPGRADE = 0x02 ACTION_UPLOAD_FOR_COMPARE = 0x03 CC_LONG_DURATION_CMD_IN_PROGRESS = 0x80 CC_GET_COMP_PROP_UPGRADE_NOT_SUPPORTED_OVER_INTF = 0x81 CC_GET_COMP_PROP_INVALID_COMPONENT = 0x82 CC_GET_COMP_PROP_INVALID_PROPERTIES_SELECTOR = 0x83 CC_INITIATE_UPGRADE_CMD_IN_PROGRESS = 0x80 CC_INITIATE_UPGRADE_INVALID_COMPONENT = 0x81 CC_QUERY_SELFTEST_COMPLETED = 0x00 CC_QUERY_SELFTEST_IN_PROGRESS = 0x80 CC_QUERY_SELFTEST_UPGRADE_NOT_SUPPORTED_OVER_INTF = 0x81 CC_QUERY_SELFTEST_NO_RESULTS_AVAILABLE = 0xD5 CC_ABORT_UPGRADE_CANNOT_ABORT = 0x80 CC_ABORT_UPGRADE_CANNOT_RESUME_OPERATION = 0x81 class Hpm(object): @staticmethod def _get_component_count(components): """Return the number of components.""" return bin(components).count('1') def get_target_upgrade_capabilities(self): rsp = self.send_message_with_name('GetTargetUpgradeCapabilities') return TargetUpgradeCapabilities(rsp) def get_component_property(self, component_id, property_id): rsp = self.send_message_with_name('GetComponentProperties', id=component_id, selector=property_id) return ComponentProperty.from_data(property_id, rsp.data) def get_component_properties(self, component_id): properties = [] for p in (PROPERTY_GENERAL_PROPERTIES, PROPERTY_CURRENT_VERSION, PROPERTY_DESCRIPTION_STRING, PROPERTY_ROLLBACK_VERSION, PROPERTY_DEFERRED_VERSION): try: prop = self.get_component_property(component_id, p) if prop is not None: properties.append(prop) except CompletionCodeError as e: if e.cc == CC_GET_COMP_PROP_INVALID_PROPERTIES_SELECTOR: continue return properties def find_component_id_by_descriptor(self, descriptor): caps = self.get_target_upgrade_capabilities() for component_id in caps.components: prop = self.get_component_property(component_id, PROPERTY_DESCRIPTION_STRING) if prop is not None: if prop.description == descriptor: return component_id return None def abort_firmware_upgrade(self): self.send_message_with_name('AbortFirmwareUpgrade') def initiate_upgrade_action(self, components_mask, action): """Initiate Upgrade Action. components: action: ACTION_BACKUP_COMPONENT = 0x00 ACTION_PREPARE_COMPONENT = 0x01 ACTION_UPLOAD_FOR_UPGRADE = 0x02 ACTION_UPLOAD_FOR_COMPARE = 0x03 """ if action in (ACTION_UPLOAD_FOR_UPGRADE, ACTION_UPLOAD_FOR_COMPARE): if self._get_component_count(components_mask) != 1: raise HpmError("more than 1 component not support for action") self.send_message_with_name('InitiateUpgradeAction', components=components_mask, action=action) def initiate_upgrade_action_and_wait(self, components_mask, action, timeout=2, interval=0.1): """Initiate Upgrade Action and wait for long running command.""" try: self.initiate_upgrade_action(components_mask, action) except CompletionCodeError as e: if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS: self.wait_for_long_duration_command( constants.CMDID_HPM_INITIATE_UPGRADE_ACTION, timeout, interval) else: raise HpmError('initiate_upgrade_action CC=0x%02x' % e.cc) def upload_firmware_block(self, block_number, data): data = [ord(c) for c in data] self.send_message_with_name('UploadFirmwareBlock', number=block_number, data=data) @staticmethod def _determine_max_block_size(): return 22 def upload_binary(self, binary, timeout=2, interval=0.1, retry=3): """Upload all firmware blocks from a binary.""" block_number = 0 block_size = self._determine_max_block_size() for chunk in chunks(binary, block_size): try: self.upload_firmware_block(block_number, chunk) except CompletionCodeError as e: if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS: self.wait_for_long_duration_command( constants.CMDID_HPM_UPLOAD_FIRMWARE_BLOCK, timeout, interval) else: raise HpmError('upload_firmware_block CC=0x%02x' % e.cc) except IpmiTimeoutError: retry -= 1 if retry == 0: raise IpmiTimeoutError() block_number += 1 block_number &= 0xff def finish_firmware_upload(self, component, length): return self.send_message_with_name('FinishFirmwareUpload', component_id=component, image_length=length) def finish_upload_and_wait(self, component, length, timeout=2, interval=0.1): """Finish, upload and for the firmware.""" try: rsp = self.finish_firmware_upload(component, length) check_completion_code(rsp.completion_code) except CompletionCodeError as e: if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS: self.wait_for_long_duration_command( constants.CMDID_HPM_FINISH_FIRMWARE_UPLOAD, timeout, interval) else: raise HpmError('finish_firmware_upload CC=0x%02x' % e.cc) def get_upgrade_status(self): return UpgradeStatus(self.send_message_with_name('GetUpgradeStatus')) def wait_for_long_duration_command(self, expected_cmd, timeout, interval): start_time = time.time() while time.time() < start_time + timeout: try: status = self.get_upgrade_status() if status.command_in_progress is not expected_cmd \ and status.command_in_progress != 0x34: pass if status.last_completion_code \ == CC_LONG_DURATION_CMD_IN_PROGRESS: time.sleep(interval) else: return except IpmiTimeoutError: time.sleep(interval) except IOError: time.sleep(interval) def activate_firmware(self, rollback_override=None): req = create_request_by_name('ActivateFirmware') if rollback_override is not None: req.rollback_override_policy = rollback_override rsp = self.send_message(req) check_completion_code(rsp.completion_code) def activate_firmware_and_wait(self, rollback_override=None, timeout=2, interval=1): """Activate and wait for the new uploaded firmware.""" try: self.activate_firmware(rollback_override) except CompletionCodeError as e: if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS: self.wait_for_long_duration_command( constants.CMDID_HPM_ACTIVATE_FIRMWARE, timeout, interval) else: raise HpmError('activate_firmware CC=0x%02x' % e.cc) except IpmiTimeoutError: # controller is in reset and flashed new firmware pass def query_selftest_results(self): return SelfTestResult( self.send_message_with_name('QuerySelftestResults')) def query_rollback_status(self): return RollbackStatus( self.send_message_with_name('QueryRollbackStatus')) def initiate_manual_rollback(self): return RollbackStatus( self.send_message_with_name('InitiateManualRollback')) def initiate_manual_rollback_and_wait(self, timeout=2, interval=0.1): try: self.initiate_manual_rollback() except CompletionCodeError as e: if e.cc == CC_LONG_DURATION_CMD_IN_PROGRESS: self.wait_for_long_duration_command( constants.CMDID_HPM_INITIATE_MANUAL_ROLLBACK, 60, interval) else: raise HpmError('activate_firmware CC=0x%02x' % e.cc) except IpmiTimeoutError: # controller is in reset and flashed new firmware pass @staticmethod def open_upgrade_image(filename): return UpgradeImage(filename) @staticmethod def get_upgrade_version_from_file(filename): image = UpgradeImage(filename) for action in image.actions: if isinstance(action, UpgradeActionRecordUploadForUpgrade): return action.firmware_version return None @staticmethod def _do_upgrade_action_backup(image): for action in image.actions: if isinstance(action, UpgradeActionRecordBackup): pass @staticmethod def _do_upgrade_action_prepare(image): for action in image.actions: if isinstance(action, UpgradeActionRecordPrepare): print("do ACTION_PREPARE_COMPONENT") @staticmethod def _do_upgrade_action_upload(image): for action in image.actions: if isinstance(action, UpgradeActionRecordUploadForUpgrade): print("do ACTION_UPLOAD_FOR_UPGRADE") def preparation_stage(self, image): #################################################### # match device ID, manfuacturer ID, etc. device_id = self.get_device_id() header = image.header if header.device_id != device_id.device_id: raise HpmError('Device ID: image=0x%x device=0x%x' % (header.device_id, device_id.device_id)) if header.manufacturer_id != device_id.manufacturer_id: raise HpmError('Manufacturer ID: image=0x%x device=0x%x' % (header.manufacturer_id, device_id.manufacturer_id)) if header.product_id != device_id.product_id: raise HpmError('Product ID: image=0x%x device=0x%x' % (header.product_id, device_id.product_id)) # tbd check version #################################################### # compare current revision with upgrade image earlist comp rev targetCap = self.get_target_upgrade_capabilities() # tbd check version #################################################### # Match IPM Controller capabilities with Upgrade Image capabilities support = False for imageComponent in header.components: if imageComponent in targetCap.components: support = True if support is not True: raise HpmError('no supported component in image') def upgrade_stage(self, image, component): for action in image.actions: if action.components & (1 << component) == 0: continue self.initiate_upgrade_action_and_wait(1 << component, action.action_type) if isinstance(action, UpgradeActionRecordUploadForUpgrade): self.upload_binary(action.firmware_image_data) self.finish_upload_and_wait(component, action.firmware_length) def _activation_state_do_self_testing(self): pass def wait_until_new_firmware_comes_up(self, timeout, interval): start_time = time.time() while time.time() < start_time + timeout: try: self.get_upgrade_status() self.get_device_id() except IpmiTimeoutError: time.sleep(interval) except IOError: time.sleep(interval) time.sleep(5) def activation_stage(self, image, component): self.activate_firmware_and_wait( image.header.inaccessibility_timeout, 1) self.wait_until_new_firmware_comes_up( image.header.inaccessibility_timeout, 1) self._activation_state_do_self_testing() def install_component_from_image(self, image, component): self.abort_firmware_upgrade() if component not in image.header.components: raise HpmError('component=%d not in image' % component) self.preparation_stage(image) self.upgrade_stage(image, component) self.activation_stage(image, component) def install_component_from_file(self, filename, component): image = UpgradeImage(filename) self.install_component_from_image(image, component) class UpgradeStatus(State): def _from_response(self, rsp): self.command_in_progress = rsp.command_in_progress self.last_completion_code = rsp.last_completion_code def __str__(self): string = [] string.append("cmd=0x%02x cc=0x%02x" % (self.command_in_progress, self.last_completion_code)) return "\n".join(string) class TargetUpgradeCapabilities(State): def _from_response(self, rsp): self.version = rsp.hpm_1_version self.components = [] for i in range(8): if rsp.component_present & (1 << i): self.components.append(i) def __str__(self): string = [] string.append("Target Upgrade Capabilities") string.append(" HPM.1 version: %s" % self.version) string.append(" Components: %s" % self.components) return "\n".join(string) codecs.register(bcd_search) class ComponentProperty(object): def __init__(self, data=None): if (data): self._from_rsp_data(data) @staticmethod def from_data(component_id, data): if isinstance(data, str): data = [ord(c) for c in data] if component_id is PROPERTY_GENERAL_PROPERTIES: return ComponentPropertyGeneral(data) elif component_id is PROPERTY_CURRENT_VERSION: return ComponentPropertyCurrentVersion(data) elif component_id is PROPERTY_DESCRIPTION_STRING: return ComponentPropertyDescriptionString(data) elif component_id is PROPERTY_ROLLBACK_VERSION: return ComponentPropertyRollbackVersion(data) elif component_id is PROPERTY_DEFERRED_VERSION: return ComponentPropertyDeferredVersion(data) elif component_id in PROPERTY_OEM: raise NotImplementedError class ComponentPropertyGeneral(ComponentProperty): ROLLBACK_SUPPORT_MASK = 0x03 PREPARATION_SUPPORT_MASK = 0x04 COMPARISON_SUPPORT_MASK = 0x08 DEFERRED_ACTIVATION_SUPPORT_MASK = 0x10 PAYLOAD_COLD_RESET_REQ_SUPPORT_MASK = 0x20 def _from_rsp_data(self, data): support = [] cap = data[0] if cap & self.ROLLBACK_SUPPORT_MASK == 0: support.append('rollback_backup_not_supported') elif cap & self.ROLLBACK_SUPPORT_MASK == 1: support.append('rollback_is_supported') elif cap & self.ROLLBACK_SUPPORT_MASK == 2: support.append('rollback_is_supported') elif cap & self.ROLLBACK_SUPPORT_MASK == 3: support.append('reserved') if cap & self.PREPARATION_SUPPORT_MASK: support.append('prepartion') if cap & self.COMPARISON_SUPPORT_MASK: support.append('comparison') if cap & self.DEFERRED_ACTIVATION_SUPPORT_MASK: support.append('deferred_activation') if cap & self.PAYLOAD_COLD_RESET_REQ_SUPPORT_MASK: support.append('payload_cold_reset_required') self.general = support class ComponentPropertyCurrentVersion(ComponentProperty): def _from_rsp_data(self, data): self.version = VersionField(data) class ComponentPropertyDescriptionString(ComponentProperty): def _from_rsp_data(self, data): descr = py3_array_tobytes(array('B', data)) descr = py3dec_unic_bytes_fix(descr) # strip '\x00' descr = descr.replace('\0', '') self.description = descr class ComponentPropertyRollbackVersion(ComponentProperty): def _from_rsp_data(self, data): self.version = VersionField(data) class ComponentPropertyDeferredVersion(ComponentProperty): def _from_rsp_data(self, data): self.version = VersionField(data) class ComponentPropertyOem(ComponentProperty): def _from_rsp_data(self, data): self.oem_data = data class SelfTestResult(State): CORRUPTED_OR_INACCESSIBLE_DATA_OR_DEVICES = 0x57 def _from_response(self, rsp): self.status = rsp.selftest_result_1 result2 = rsp.selftest_result_2 if self.status != self.CORRUPTED_OR_INACCESSIBLE_DATA_OR_DEVICES: self.fail_sel = (result2 & 0x80) >> 7 self.fail_sdrr = (result2 & 0x40) >> 6 self.fail_bmc_fru = (result2 & 0x20) >> 5 self.fail_ipmb = (result2 & 0x10) >> 4 self.fail_sdrr_empty = (result2 & 0x08) >> 3 self.fail_bmc_fru_interanl_area = (result2 & 0x04) >> 2 self.fail_bootblock = (result2 & 0x02) >> 1 self.fail_mc = (result2 & 0x01) >> 0 class RollbackStatus(object): def __init__(self, rsp=None): if rsp: self._from_rsp(rsp) def _from_rsp(self, rsp): if rsp.completion_estimate: self.percent_complete = rsp.completion_estimate image_header = collections.namedtuple('image_header', ['field_name', 'format', 'start', 'len']) class UpgradeImageHeaderRecord(object): FORMAT = [ image_header('format_version', 'B', 8, 1), image_header('device_id', 'B', 9, 1), image_header('product_id', '> 1) if enable_i2c_pullups: self.enable_pullups(enable_i2c_pullups) if enable_target_power: self.enable_target_power(enable_target_power) def enable_pullups(self, enabled): self._dev.i2c_pullups = enabled def enable_target_power(self, enabled): self._dev.target_power = enabled def raw_write(self, address, data): self._dev.i2c_master_write(address, data) def establish_session(self, session): # just remember session parameters here self._session = session def close_session(self): self._dev.close() def is_ipmc_accessible(self, target): header = IpmbHeaderReq() header.netfn = 6 header.rs_lun = 0 header.rs_sa = target.ipmb_address header.rq_seq = self.next_sequence_number header.rq_lun = 0 header.rq_sa = self.slave_address header.cmdid = 1 self._send_raw(header, None) self._receive_raw(header) return True def _inc_sequence_number(self): self.next_sequence_number = (self.next_sequence_number + 1) % 64 @staticmethod def _encode_ipmb_msg_req(header, cmd_data): data = header.encode() data.extend(cmd_data) data.append(checksum(data[2:])) return data def _send_raw(self, header, raw_bytes): raw_bytes = encode_ipmb_msg(header, raw_bytes) i2c_addr = header.rs_sa >> 1 raw_bytes = array('B', raw_bytes) log().debug('I2C TX to %02Xh [%s]', i2c_addr, ' '.join(['%02x' % b for b in raw_bytes])) self._dev.i2c_master_write(i2c_addr, raw_bytes[1:]) def _receive_raw(self, header): start_time = time.time() rsp_received = False poll_returned_no_data = False while not rsp_received: timeout = self.timeout - (time.time() - start_time) if timeout <= 0 or poll_returned_no_data: raise IpmiTimeoutError() ret = self._dev.poll(int(timeout * 1000)) # poll returns an empty list if no event is pending if not ret: poll_returned_no_data = True continue (i2c_addr, rx_data) = self._dev.i2c_slave_read() rx_data = array('B', rx_data) log().debug('I2C RX from %02Xh [%s]', i2c_addr << 1, ' '.join(['%02x' % c for c in rx_data])) rq_sa = array('B', [i2c_addr << 1, ]) rsp_received = rx_filter(header, rq_sa + rx_data) return rx_data def _send_and_receive(self, target, lun, netfn, cmdid, payload): """Send and receive data using aardvark interface. target: lun: netfn: cmdid: payload: IPMI message payload as bytestring Returns the received data as bytestring """ self._inc_sequence_number() # assemble IPMB header header = IpmbHeaderReq() header.netfn = netfn header.rs_lun = lun header.rs_sa = target.ipmb_address header.rq_seq = self.next_sequence_number header.rq_lun = 0 header.rq_sa = self.slave_address header.cmdid = cmdid retries = 0 while retries < self.max_retries: try: self._send_raw(header, payload) rx_data = self._receive_raw(header) break except IpmiTimeoutError: pass except IOError: pass retries += 1 time.sleep(retries*0.2) else: raise IpmiTimeoutError() return py3_array_tobytes(rx_data)[5:-1] def send_and_receive_raw(self, target, lun, netfn, raw_bytes): """Interface function to send and receive raw message. target: IPMI target lun: logical unit number netfn: network function raw_bytes: RAW bytes as bytestring Returns the IPMI message response bytestring. """ return self._send_and_receive(target=target, lun=lun, netfn=netfn, cmdid=array('B', raw_bytes)[0], payload=raw_bytes[1:]) def send_and_receive(self, req): """Interface function to send and receive an IPMI message. target: IPMI target req: IPMI message request Returns the IPMI message response. """ log().debug('IPMI Request [%s]', req) rx_data = self._send_and_receive(target=req.target, lun=req.lun, netfn=req.netfn, cmdid=req.cmdid, payload=encode_message(req)) rsp = create_message(req.netfn + 1, req.cmdid, req.group_extension) decode_message(rsp, rx_data) log().debug('IPMI Response [%s])', rsp) return rsp python-ipmi-0.5.4/pyipmi/interfaces/ipmb.py000066400000000000000000000171701436677633700207640ustar00rootroot00000000000000# Copyright (c) 2015 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from array import array from ..logger import log from ..msgs import (create_message, create_request_by_name, encode_message, decode_message, constants) from ..utils import check_completion_code from ..utils import py3_array_tobytes, py3_array_frombytes def checksum(data): """Calculate the checksum.""" csum = 0 for b in data: csum += b return -csum % 256 class IpmbHeader(object): """Representation of the IPMI message header. Request: *-------*--------------*----------*-------*---------------*-------* | rs_sa | netfn/rs_lun | checksum | rq_sa | rq_seq/rq_lun | cmdid | *-------*--------------*----------*-------*---------------*-------* Response: *-------*--------------*----------*-------*---------------*-------* | rq_sa | netfn/rq_lun | checksum | rs_sa | rq_seq/rs_lun | cmdid | *-------*--------------*----------*-------*---------------*-------* """ def __init__(self, data=None): if data: self.decode(data) rs_sa = None rs_lun = None rq_sa = None rq_lun = None rq_seq = None netfn = None cmdid = None checksum = None class IpmbHeaderReq(IpmbHeader): """Representation of the IPMI request message header.""" def encode(self): """Encode the header.""" data = array('B') data.append(self.rs_sa) data.append(self.netfn << 2 | self.rs_lun) data.append(checksum((self.rs_sa, data[1]))) data.append(self.rq_sa) data.append(self.rq_seq << 2 | self.rq_lun) data.append(self.cmdid) return py3_array_tobytes(data) def decode(self, data): """Decode the header.""" msg = array('B') py3_array_frombytes(msg, data) self.rs_sa = msg[0] self.netfn = msg[1] >> 2 self.rs_lun = msg[1] & 3 self.checksum = msg[2] self.rq_sa = msg[3] self.rq_seq = msg[4] >> 2 self.rq_lun = msg[4] & 3 self.cmdid = msg[5] class IpmbHeaderRsp(IpmbHeader): """Representation of the IPMI response message header.""" def encode(self): """Encode the header.""" data = array('B') data.append(self.rq_sa) data.append(self.netfn << 2 | self.rq_lun) data.append(checksum((self.rq_sa, data[1]))) data.append(self.rs_sa) data.append(self.rq_seq << 2 | self.rs_lun) data.append(self.cmdid) return py3_array_tobytes(data) def decode(self, data): """Decode the header.""" data = array('B', data) self.rq_sa = data[0] self.netfn = data[1] >> 2 self.rq_lun = data[1] & 3 self.checksum = data[2] self.rs_sa = data[3] self.rq_seq = data[4] >> 2 self.rs_lun = data[4] & 3 self.cmdid = data[5] def from_req_header(self, req_header): self.rs_lun = req_header.rq_lun self.rs_sa = req_header.rq_sa self.rq_seq = req_header.rq_seq self.rq_lun = req_header.rs_lun self.rq_sa = req_header.rs_sa self.netfn = req_header.netfn self.cmdid = req_header.cmdid def encode_ipmb_msg(header, data): """Encode an IPMB message. header: IPMB header object data: IPMI message data as bytestring Returns the message as bytestring. """ msg = array('B') py3_array_frombytes(msg, header.encode()) if data is not None: a = array('B') py3_array_frombytes(a, data) msg.extend(a) msg.append(checksum(msg[3:])) return py3_array_tobytes(msg) def encode_send_message(payload, rq_sa, rs_sa, channel, seq, tracking=1): """Encode a send message command and embedd the message to be send. payload: the message to be send as bytestring rq_sa: the requester source address rs_sa: the responder source address channel: the channel seq: the sequence number tracking: tracking Returns an encode send message as bytestring """ req = create_request_by_name('SendMessage') req.channel.number = channel req.channel.tracking = tracking data = encode_message(req) header = IpmbHeaderReq() header.netfn = req.__netfn__ header.rs_lun = 0 header.rs_sa = rs_sa header.rq_seq = seq header.rq_lun = 0 header.rq_sa = rq_sa header.cmdid = req.__cmdid__ return encode_ipmb_msg(header, data + payload) def encode_bridged_message(routing, header, payload, seq): """Encode a (multi-)bridged command and embedd the message to be send. routing: payload: the message to be send as bytestring header: seq: the sequence number Returns the encoded send message as bytestring """ # change header requester addresses for bridging header.rq_sa = routing[-1].rq_sa header.rs_sa = routing[-1].rs_sa tx_data = encode_ipmb_msg(header, payload) for bridge in reversed(routing[:-1]): tx_data = encode_send_message(tx_data, rq_sa=bridge.rq_sa, rs_sa=bridge.rs_sa, channel=bridge.channel, seq=seq) return tx_data def decode_bridged_message(rx_data): """Decode a (multi-)bridged command. rx_data: the received message as bytestring Returns the decoded message as bytestring """ while array('B', rx_data)[5] == constants.CMDID_SEND_MESSAGE: rsp = create_message(constants.NETFN_APP + 1, constants.CMDID_SEND_MESSAGE, None) decode_message(rsp, rx_data[6:]) check_completion_code(rsp.completion_code) rx_data = rx_data[7:-1] if len(rx_data) < 6: break return rx_data def rx_filter(header, data): """Check if the message in rx_data matches to the information in header. The following checks are done: - Header checksum - Payload checksum - NetFn matching - LUN matching - Command Id matching header: the header to compare with data: the received message as bytestring """ rsp_header = IpmbHeaderRsp(data=data) data = array('B', data) checks = [ (checksum(data[0:3]), 0, 'Header checksum failed'), (checksum(data[3:]), 0, 'payload checksum failed'), # rsp_header.rq_sa, header.rq_sa, 'slave address mismatch'), (rsp_header.netfn, header.netfn | 1, 'NetFn mismatch'), # rsp_header.rs_sa, header.rs_sa, 'target address mismatch'), # rsp_header.rq_lun, header.rq_lun, 'request LUN mismatch'), (rsp_header.rs_lun, header.rs_lun, 'responder LUN mismatch'), (rsp_header.rq_seq, header.rq_seq, 'sequence number mismatch'), (rsp_header.cmdid, header.cmdid, 'command id mismatch'), ] match = True for left, right, msg in checks: if left != right: log().debug('{:s}: {:d} {:d}'.format(msg, left, right)) match = False return match python-ipmi-0.5.4/pyipmi/interfaces/ipmbdev.py000066400000000000000000000123421436677633700214570ustar00rootroot00000000000000import os import select import time from array import array from ..msgs import create_message, encode_message, decode_message from ..errors import IpmiTimeoutError from ..logger import log from ..interfaces.ipmb import IpmbHeaderReq, checksum, rx_filter, encode_ipmb_msg class IpmbDev(object): """This interface uses ipmb-dev-int linux driver.""" NAME = 'ipmbdev' def __init__(self, slave_address=0x20, port='/dev/ipmb-0'): # TODO: slave address is currently not defined here self.slave_address = slave_address self.timeout = 0.25 self.max_retries = 3 self.next_sequence_number = 0 self._dev = os.open(port, os.O_RDWR) def establish_session(self, session): # just remember session parameters here self._session = session def close_session(self): os.close(self._dev) def is_ipmc_accessible(self, target): header = IpmbHeaderReq() header.netfn = 6 header.rs_lun = 0 header.rs_sa = target.ipmb_address header.rq_seq = self.next_sequence_number header.rq_lun = 0 header.rq_sa = self.slave_address header.cmdid = 1 self._send_raw(header, None) self._receive_raw(header) return True def _inc_sequence_number(self): self.next_sequence_number = (self.next_sequence_number + 1) % 64 @staticmethod def _encode_ipmb_msg_req(header, cmd_data): data = header.encode() data.extend(cmd_data) data.append(checksum(data[2:])) return data def _send_raw(self, header, raw_bytes): raw_bytes = encode_ipmb_msg(header, raw_bytes) i2c_addr = header.rs_sa >> 1 log().debug('I2C TX to %02Xh [%s]', i2c_addr, ' '.join(['%02x' % b for b in raw_bytes])) os.write(self._dev, bytes([len(raw_bytes)]) + raw_bytes) def _receive_raw(self, header): start_time = time.time() rsp_received = False poll_returned_no_data = False while not rsp_received: timeout = self.timeout - (time.time() - start_time) if timeout <= 0 or poll_returned_no_data: raise IpmiTimeoutError() r, w, e = select.select([self._dev], [], [], timeout) if self._dev not in r: poll_returned_no_data = True continue rx_data = os.read(self._dev, 256) # ipmb-dev-int puts message length into first byte assert rx_data[0] == len(rx_data) - 1 rx_data = rx_data[1:] rx_data = array('B', rx_data) log().debug('I2C RX from %02Xh [%s]', rx_data[3], ' '.join(['%02x' % c for c in rx_data])) rsp_received = rx_filter(header, rx_data) rx_data = rx_data[1:] return rx_data def _send_and_receive(self, target, lun, netfn, cmdid, payload): """Send and receive data using ipmb-dev-int interface. target: lun: netfn: cmdid: payload: IPMI message payload as bytestring Returns the received data as bytestring """ self._inc_sequence_number() # assemble IPMB header header = IpmbHeaderReq() header.netfn = netfn header.rs_lun = lun header.rs_sa = target.ipmb_address header.rq_seq = self.next_sequence_number header.rq_lun = 0 header.rq_sa = self.slave_address header.cmdid = cmdid retries = 0 while retries < self.max_retries: try: self._send_raw(header, payload) rx_data = self._receive_raw(header) break except IpmiTimeoutError: pass except IOError: pass retries += 1 time.sleep(retries * 0.2) else: raise IpmiTimeoutError() return rx_data[5:-1] def send_and_receive_raw(self, target, lun, netfn, raw_bytes): """Interface function to send and receive raw message. target: IPMI target lun: logical unit number netfn: network function raw_bytes: RAW bytes as bytestring Returns the IPMI message response bytestring. """ return self._send_and_receive(target=target, lun=lun, netfn=netfn, cmdid=array('B', raw_bytes)[0], payload=raw_bytes[1:]) def send_and_receive(self, req): """Interface function to send and receive an IPMI message. target: IPMI target req: IPMI message request Returns the IPMI message response. """ log().debug('IPMI Request [%s]', req) rx_data = self._send_and_receive(target=req.target, lun=req.lun, netfn=req.netfn, cmdid=req.cmdid, payload=encode_message(req)) rsp = create_message(req.netfn + 1, req.cmdid, req.group_extension) decode_message(rsp, rx_data) log().debug('IPMI Response [%s])', rsp) return rsp python-ipmi-0.5.4/pyipmi/interfaces/ipmitool.py000066400000000000000000000236511436677633700216720ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import re from subprocess import Popen, PIPE from array import array from ..session import Session from ..errors import IpmiTimeoutError, IpmiConnectionError from ..logger import log from ..msgs import encode_message, decode_message, create_message from ..msgs.constants import CC_OK from ..utils import py3dec_unic_bytes_fix, ByteBuffer, py3_array_tobytes class Ipmitool(object): """This interface uses the ipmitool raw command. This "emulates" a RMCP session by using raw commands. It uses the session information to assemble the correct ipmitool parameters. Therefore, a session has to be established before any request can be sent. """ NAME = 'ipmitool' IPMITOOL_PATH = 'ipmitool' supported_interfaces = ['lan', 'lanplus', 'serial-terminal', 'open'] def __init__(self, interface_type='lan'): if interface_type in self.supported_interfaces: self._interface_type = interface_type else: raise RuntimeError('interface type %s not supported' % interface_type) self.re_completion_code = re.compile( r"Unable to send RAW command \(.*rsp=(0x[0-9a-f]+)\)") self.re_timeout = re.compile( r"Unable to send RAW command \(.*cmd=0x[0-9a-f]+\)") self.re_unable_establish = re.compile( r".*Unable to establish.*") self.re_could_not_open = re.compile( r".*Could not open device.*") self._session = None def establish_session(self, session): # just remember session parameters here self._session = session def rmcp_ping(self): if self._interface_type == 'serial-terminal': raise RuntimeError( 'rcmp_ping not supported on "serial-terminal" interface') # for now this uses impitool.. cmd = self.IPMITOOL_PATH cmd += (' -I %s' % self._interface_type) cmd += (' -H %s' % self._session.rmcp_host) cmd += (' -p %s' % self._session.rmcp_port) if self._session.auth_type == Session.AUTH_TYPE_NONE: cmd += (' -A NONE') elif self._session.auth_type == Session.AUTH_TYPE_PASSWORD: cmd += (' -U "%s"' % self._session.auth_username) cmd += (' -P "%s"' % self._session.auth_password) cmd += (' session info all') _, rc = self._run_ipmitool(cmd) if rc: raise IpmiTimeoutError() def is_ipmc_accessible(self, target): try: self.rmcp_ping() accessible = True except IpmiTimeoutError: accessible = False return accessible def _parse_output(self, output): cc, rsp = None, None hexstr = '' for line in py3dec_unic_bytes_fix(output).split('\n'): # Don't try to parse ipmitool error messages if 'failed' in line: continue # Check for timeout if self.re_timeout.match(line): raise IpmiTimeoutError() # Check for unable to establish session if self.re_unable_establish.match(line): raise IpmiConnectionError('ipmitool: {}'.format(line)) # Check for completion code match_completion_code = self.re_completion_code.match(line) if match_completion_code: cc = int(match_completion_code.group(1), 16) break # Check for error opening ipmi device if self.re_could_not_open.match(line): raise RuntimeError('ipmitool failed: {}'.format(output)) hexstr += line.replace('\r', '').strip() + ' ' hexstr = hexstr.strip() if len(hexstr): rsp = array('B', [ int(value, 16) for value in hexstr.split(' ') ]) return cc, rsp def send_and_receive_raw(self, target, lun, netfn, raw_bytes): if self._interface_type in ['lan', 'lanplus']: cmd = self._build_ipmitool_cmd(target, lun, netfn, raw_bytes) elif self._interface_type in ['open']: cmd = self._build_open_ipmitool_cmd(target, lun, netfn, raw_bytes) elif self._interface_type in ['serial-terminal']: cmd = self._build_serial_ipmitool_cmd(target, lun, netfn, raw_bytes) else: raise RuntimeError('interface type %s not supported' % self._interface_type) output, rc = self._run_ipmitool(cmd) cc, rsp = self._parse_output(output) data = array('B') if cc is not None: data.append(cc) else: if rc != 0: raise RuntimeError('ipmitool failed with rc=%d' % rc) # completion code data.append(CC_OK) if rsp: data.extend(rsp) log().debug('IPMI RX: {:s}'.format( ''.join('%02x ' % b for b in array('B', data)))) return py3_array_tobytes(data) def send_and_receive(self, req): log().debug('IPMI Request [%s]', req) req_data = ByteBuffer((req.cmdid,)) req_data.push_string(encode_message(req)) rsp_data = self.send_and_receive_raw(req.target, req.lun, req.netfn, py3_array_tobytes(req_data)) rsp = create_message(req.netfn + 1, req.cmdid, req.group_extension) decode_message(rsp, rsp_data) log().debug('IPMI Response [%s])', rsp) return rsp @staticmethod def _build_ipmitool_raw_data(lun, netfn, raw): cmd = ' -l {:d} raw '.format(lun) cmd += ' '.join(['0x%02x' % (d) for d in [netfn] + array('B', raw).tolist()]) return cmd @staticmethod def _build_ipmitool_target(target): cmd = '' if target is None: return '' if target.routing is not None: # we have to do bridging here if len(target.routing) == 1: pass if len(target.routing) == 2: # ipmitool/shelfmanager does implicit bridging cmd += (' -t 0x%02x' % target.routing[1].rs_sa) cmd += (' -b %d' % target.routing[0].channel) elif len(target.routing) == 3: cmd += (' -T 0x%02x' % target.routing[1].rs_sa) cmd += (' -B %d' % target.routing[0].channel) cmd += (' -t 0x%02x' % target.routing[2].rs_sa) cmd += (' -b %d' % target.routing[1].channel) else: raise RuntimeError('The impitool interface at most double ' 'briding %s' % target) elif target.ipmb_address: cmd += (' -t 0x%02x' % target.ipmb_address) return cmd def _build_ipmitool_cmd(self, target, lun, netfn, raw_bytes): if not hasattr(self, '_session'): raise RuntimeError('Session needs to be set') cmd = self.IPMITOOL_PATH cmd += (' -I %s' % self._interface_type) cmd += (' -H %s' % self._session.rmcp_host) cmd += (' -p %s' % self._session.rmcp_port) if self._session.auth_type == Session.AUTH_TYPE_NONE: cmd += ' -P ""' elif self._session.auth_type == Session.AUTH_TYPE_PASSWORD: cmd += (' -U "%s"' % self._session.auth_username) cmd += (' -P "%s"' % self._session.auth_password) else: raise RuntimeError('Session type %d not supported' % self._session.auth_type) cmd += self._build_ipmitool_target(target) cmd += self._build_ipmitool_raw_data(lun, netfn, raw_bytes) cmd += (' 2>&1') return cmd def _build_serial_ipmitool_cmd(self, target, lun, netfn, raw_bytes): if not hasattr(self, '_session'): raise RuntimeError('Session needs to be set') cmd = '{path!s:s} -I {interface!s:s} -D {port!s:s}:{baud!s:s}'\ .format( path=self.IPMITOOL_PATH, interface=self._interface_type, port=self._session.serial_port, baud=self._session.serial_baudrate ) cmd += self._build_ipmitool_target(target) cmd += self._build_ipmitool_raw_data(lun, netfn, raw_bytes) return cmd def _build_open_ipmitool_cmd(self, target, lun, netfn, raw_bytes): if not hasattr(self, '_session'): raise RuntimeError('Session needs to be set') cmd = self.IPMITOOL_PATH cmd += (' -I %s' % self._interface_type) cmd += self._build_ipmitool_target(target) cmd += self._build_ipmitool_raw_data(lun, netfn, raw_bytes) cmd += (' 2>&1') return cmd @staticmethod def _run_ipmitool(cmd): """Legacy call of ipmitool (will be removed in future).""" log().debug('Running ipmitool "%s"', cmd) child = Popen(cmd, shell=True, stdout=PIPE) output = child.communicate()[0] log().debug('return with rc=%d, output was:\n%s', child.returncode, output) if child.returncode == 127: raise RuntimeError('ipmitool command not found') return output, child.returncode python-ipmi-0.5.4/pyipmi/interfaces/mock.py000066400000000000000000000005531436677633700207630ustar00rootroot00000000000000class Mock(object): """This interface is used as mock.""" NAME = 'mock' def __init__(self): pass def establish_session(self, session): pass def is_ipmc_accessible(self, target): pass def send_and_receive_raw(self, target, lun, netfn, raw_bytes): pass def send_and_receive(self, req): pass python-ipmi-0.5.4/pyipmi/interfaces/rmcp.py000066400000000000000000000472401436677633700207770ustar00rootroot00000000000000# Copyright (c) 2018 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import socket import struct import hashlib import random import threading from array import array from queue import Queue from .. import Target from ..session import Session from ..msgs import (create_message, create_request_by_name, encode_message, decode_message, constants) from ..messaging import ChannelAuthenticationCapabilities from ..errors import DecodingError, NotSupportedError from ..logger import log from ..interfaces.ipmb import (IpmbHeaderReq, encode_ipmb_msg, encode_bridged_message, decode_bridged_message, rx_filter) from ..utils import check_completion_code, py3_array_tobytes CLASS_NORMAL_MSG = 0x00 CLASS_ACK_MSG = 0x80 RMCP_CLASS_ASF = 0x06 RMCP_CLASS_IPMI = 0x07 RMCP_CLASS_OEM = 0x08 def call_repeatedly(interval, func, *args): stopped = threading.Event() def loop(): # the first call is in `interval` secs while not stopped.wait(interval): try: func(*args) except socket.timeout: pass t = threading.Thread(target=loop) t.daemon = True t.start() return stopped.set class RmcpMsg(object): RMCP_HEADER_FORMAT = '!BxBB' ASF_RMCP_V_1_0 = 6 version = None seq_number = None class_of_msg = None def __init__(self, class_of_msg=None): if class_of_msg is not None: self.class_of_msg = class_of_msg def pack(self, sdu, seq_number): pdu = struct.pack(self.RMCP_HEADER_FORMAT, self.ASF_RMCP_V_1_0, seq_number, self.class_of_msg) if sdu is not None: pdu += sdu return pdu def unpack(self, pdu): header_len = struct.calcsize(self.RMCP_HEADER_FORMAT) header = pdu[:header_len] (self.version, self.seq_number, self.class_of_msg) = \ struct.unpack(self.RMCP_HEADER_FORMAT, header) sdu = pdu[header_len:] if self.version != self.ASF_RMCP_V_1_0: raise DecodingError('invalid RMCP version field') return sdu class AsfMsg(object): ASF_HEADER_FORMAT = '!IBBxB' ASF_TYPE_PRESENCE_PONG = 0x40 ASF_TYPE_PRESENCE_PING = 0x80 asf_type = 0 def __init__(self): self.iana_enterprise_number = 4542 self.tag = 0 self.data = None self.sdu = None def pack(self): if self.data: data_len = len(self.data) else: data_len = 0 pdu = struct.pack(self.ASF_HEADER_FORMAT, self.iana_enterprise_number, self.asf_type, self.tag, data_len) if self.data: pdu += self.data return pdu def unpack(self, sdu): self.sdu = sdu header_len = struct.calcsize(self.ASF_HEADER_FORMAT) header = sdu[:header_len] (self.iana_enterprise_number, self.asf_type, self.tag, data_len) = \ struct.unpack(self.ASF_HEADER_FORMAT, header) if len(sdu) < header_len + data_len: raise DecodingError('short SDU') elif len(sdu) > header_len + data_len: raise DecodingError('SDU has extra bytes') if data_len != 0: self.data = sdu[header_len:header_len + data_len] else: self.data = None if hasattr(self, 'check_header'): self.check_header() def __str__(self): if self.data: return ' '.join('%02x' % b for b in array('B', self.data)) if self.sdu: return ' '.join('%02x' % b for b in array('B', self.sdu)) return '' @staticmethod def from_data(sdu): asf = AsfMsg() asf.unpack(sdu) try: cls = { AsfMsg().ASF_TYPE_PRESENCE_PING: AsfPing, AsfMsg().ASF_TYPE_PRESENCE_PONG: AsfPong, }[asf.asf_type] except KeyError: raise DecodingError('Unsupported ASF type(0x%02x)' % asf.asf_type) instance = cls() instance.unpack(sdu) return instance class AsfPing(AsfMsg): def __init__(self): AsfMsg.__init__(self) self.asf_type = self.ASF_TYPE_PRESENCE_PING def check_header(self): if self.asf_type != self.ASF_TYPE_PRESENCE_PING: raise DecodingError('type does not match') if self.data: raise DecodingError('Data length is not zero') def __str__(self): return 'ping: ' + super(AsfMsg, self).__str__() class AsfPong(AsfMsg): DATA_FORMAT = '!IIBB6x' def __init__(self): self.asf_type = self.ASF_TYPE_PRESENCE_PONG self.oem_iana_enterprise_number = 4542 self.oem_defined = 0 self.supported_entities = 0 self.supported_interactions = 0 def pack(self): pdu = struct.pack(self.DATA_FORMAT, self.oem_iana_enterprise_number, self.oem_defined, self.supported_entities, self.supported_interactions) return pdu def unpack(self, sdu): AsfMsg.unpack(self, sdu) # header_len = struct.calcsize(self.ASF_HEADER_FORMAT) (self.oem_iana_enterprise_number, self.oem_defined, self.supported_entities, self.supported_interactions) =\ struct.unpack(self.DATA_FORMAT, self.data) self.check_data() def check_data(self): if self.oem_iana_enterprise_number == 4542 and self.oem_defined != 0: raise DecodingError('SDU malformed') if self.supported_interactions != 0: raise DecodingError('SDU malformed') def check_header(self): if self.asf_type != self.ASF_TYPE_PRESENCE_PONG: raise DecodingError('type does not match') if len(self.data) != struct.calcsize(self.DATA_FORMAT): raise DecodingError('Data length mismatch') class IpmiMsg(object): HEADER_FORMAT_NO_AUTH = '!BIIB' HEADER_FORMAT_AUTH = '!BII16BB' def __init__(self, session=None): self.session = session def _pack_session_id(self): if self.session is not None: session_id = self.session.sid else: session_id = 0 return struct.unpack("I", session_id))[0] def _pack_sequence_number(self): if self.session is not None: seq = self.session.sequence_number else: seq = 0 return struct.unpack("I", seq))[0] def _padd_password(self): """Padd the password. The password/key is 0 padded to 16-bytes for all specified authentication types. """ password = self.session._auth_password if isinstance(password, str): password = str.encode(password) return password.ljust(16, b'\x00') def _pack_auth_code_straight(self): """Return the auth code as bytestring.""" return self._padd_password() def _pack_auth_code_md5(self, sdu): auth_code = struct.pack('>16s I %ds I 16s' % len(sdu), self._pack_auth_code_straight(), self._pack_session_id(), sdu, self._pack_sequence_number(), self._pack_auth_code_straight()) return hashlib.md5(auth_code).digest() def pack(self, sdu): if sdu is not None: data_len = len(sdu) else: data_len = 0 if self.session is not None: auth_type = self.session.auth_type if self.session.activated: self.session.increment_sequence_number() else: auth_type = Session.AUTH_TYPE_NONE pdu = struct.pack('!BII', auth_type, self._pack_sequence_number(), self._pack_session_id()) if auth_type == Session.AUTH_TYPE_NONE: pass elif auth_type == Session.AUTH_TYPE_PASSWORD: pdu += self._pack_auth_code_straight() elif auth_type == Session.AUTH_TYPE_MD5: pdu += self._pack_auth_code_md5(sdu) else: raise NotSupportedError('authentication type %s' % auth_type) pdu += py3_array_tobytes(array('B', [data_len])) if sdu is not None: pdu += sdu return pdu def unpack(self, pdu): auth_type = array('B', pdu)[0] if auth_type != 0: header_len = struct.calcsize(self.HEADER_FORMAT_AUTH) header = pdu[:header_len] # TBD .. find a way to do this better self.auth_type = array('B', pdu)[0] (self.sequence_number,) = struct.unpack('!I', pdu[1:5]) (self.session_id,) = struct.unpack('!I', pdu[5:9]) self.auth_code = [a for a in struct.unpack('!16B', pdu[9:25])] data_len = array('B', pdu)[25] else: header_len = struct.calcsize(self.HEADER_FORMAT_NO_AUTH) header = pdu[:header_len] (self.auth_type, self.sequence_number, self.session_id, data_len) = struct.unpack(self.HEADER_FORMAT_NO_AUTH, header) if len(pdu) < header_len + data_len: raise DecodingError('short SDU') elif len(pdu) > header_len + data_len: raise DecodingError('SDU has extra bytes ({:d},{:d},{:d} )'.format( len(pdu), header_len, data_len)) if hasattr(self, 'check_header'): self.check_header() if data_len != 0: sdu = pdu[header_len:header_len + data_len] else: sdu = None return sdu def check_data(self): pass def check_header(self): pass class Rmcp(object): NAME = 'rmcp' _session = None def __init__(self, slave_address=0x81, host_target_address=0x20, keep_alive_interval=1): self.host = None self.port = None self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.seq_number = 0xff self.slave_address = slave_address self.host_target = Target(host_target_address) self.set_timeout(2.0) self.next_sequence_number = 0 self.keep_alive_interval = keep_alive_interval self._stop_keep_alive = None self._q = Queue() self.transaction_lock = threading.Lock() def _send_rmcp_msg(self, sdu, class_of_msg): rmcp = RmcpMsg(class_of_msg) pdu = rmcp.pack(sdu, self.seq_number) self._sock.sendto(pdu, (self.host, self.port)) if self.seq_number != 255: self.seq_number = (self.seq_number + 1) % 254 def _receive_rmcp_msg(self): (pdu, _) = self._sock.recvfrom(4096) rmcp = RmcpMsg() sdu = rmcp.unpack(pdu) return (rmcp.seq_number, rmcp.class_of_msg, sdu) def set_timeout(self, timeout): self._sock.settimeout(timeout) def _send_ipmi_msg(self, data): log().debug('IPMI TX: {:s}'.format( ' '.join('%02x' % b for b in array('B', data)))) ipmi = IpmiMsg(self._session) tx_data = ipmi.pack(data) self._send_rmcp_msg(tx_data, RMCP_CLASS_IPMI) def _receive_ipmi_msg(self): (_, class_of_msg, pdu) = self._receive_rmcp_msg() if class_of_msg != RMCP_CLASS_IPMI: raise DecodingError('invalid class field in ASF message') msg = IpmiMsg() data = msg.unpack(pdu) log().debug('IPMI RX: {:s}'.format( ' '.join('%02x' % b for b in array('B', data)))) return data def _send_asf_msg(self, msg): log().debug('ASF TX: msg') self._send_rmcp_msg(msg.pack(), RMCP_CLASS_ASF) def _receive_asf_msg(self, cls): (_, class_of_msg, data) = self._receive_rmcp_msg() log().debug('ASF RX: msg') if class_of_msg != RMCP_CLASS_ASF: raise DecodingError('invalid class field in ASF message') msg = cls() msg.unpack(data) return msg def ping(self): ping = AsfPing() self._send_asf_msg(ping) self._receive_asf_msg(AsfPong) def _get_channel_auth_cap(self): CHANNEL_NUMBER_FOR_THIS = 0xe # get channel auth cap req = create_request_by_name('GetChannelAuthenticationCapabilities') req.target = self.host_target req.channel.number = CHANNEL_NUMBER_FOR_THIS req.privilege_level.requested = Session.PRIV_LEVEL_ADMINISTRATOR rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) caps = ChannelAuthenticationCapabilities(rsp) return caps def _get_session_challenge(self, session): # get session challenge req = create_request_by_name('GetSessionChallenge') req.target = self.host_target req.authentication.type = session.auth_type if session._auth_username: req.user_name = session._auth_username.ljust(16, '\x00') rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) return rsp def _activate_session(self, session, challenge): # activate session req = create_request_by_name('ActivateSession') req.target = self.host_target req.authentication.type = session.auth_type req.privilege_level.maximum_requested =\ Session.PRIV_LEVEL_ADMINISTRATOR req.challenge_string = challenge req.session_id = self._session.sid req.initial_outbound_sequence_number = random.randrange(1, 0xffffffff) rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) return rsp def _set_session_privilege_level(self, level): req = create_request_by_name('SetSessionPrivilegeLevel') req.target = self.host_target req.privilege_level.requested = level rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) return rsp def _get_device_id(self): req = create_request_by_name('GetDeviceId') req.target = self.host_target rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) def establish_session(self, session): self._session = None self.host = session._rmcp_host self.port = session._rmcp_port # 0 - Ping self.ping() # 1 - Get Channel Authentication Capabilities log().debug('Get Channel Authentication Capabilities') caps = self._get_channel_auth_cap() log().debug('%s' % caps) # 2 - Get Session Challenge log().debug('Get Session Challenge') session.auth_type = caps.get_max_auth_type() rsp = self._get_session_challenge(session) session_challenge = rsp.challenge_string session.sid = rsp.temporary_session_id self._session = session # 3 - Activate Session log().debug('Activate Session') rsp = self._activate_session(session, session_challenge) self._session.sid = rsp.session_id self._session.sequence_number = rsp.initial_inbound_sequence_number self._session.activated = True log().debug('Set Session Privilege Level') # 4 - Set Session Privilege Level self._set_session_privilege_level(Session.PRIV_LEVEL_ADMINISTRATOR) log().debug('Session opened') if self.keep_alive_interval: self._stop_keep_alive = call_repeatedly( self.keep_alive_interval, self._get_device_id) def close_session(self): if self._stop_keep_alive: self._stop_keep_alive() if self._session.activated is False: log().debug('Session already closed') return log().debug('Close Session %s' % self._session) req = create_request_by_name('CloseSession') req.target = self.host_target req.session_id = self._session.sid rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) self._session.activated = False # self._q.join() def _inc_sequence_number(self): self.next_sequence_number = (self.next_sequence_number + 1) % 64 def _send_and_receive(self, target, lun, netfn, cmdid, payload): """Send and receive data using RMCP interface. target: lun: netfn: cmdid: raw_bytes: IPMI message payload as bytestring Returns the received data as array. """ self._inc_sequence_number() header = IpmbHeaderReq() header.netfn = netfn header.rs_lun = lun header.rs_sa = target.ipmb_address header.rq_seq = self.next_sequence_number header.rq_lun = 0 header.rq_sa = self.slave_address header.cmdid = cmdid # Bridge message if target.routing: tx_data = encode_bridged_message(target.routing, header, payload, self.next_sequence_number) else: tx_data = encode_ipmb_msg(header, payload) with self.transaction_lock: self._send_ipmi_msg(tx_data) received = False while received is False: if not self._q.empty(): rx_data = self._q.get() else: rx_data = self._receive_ipmi_msg() if array('B', rx_data)[5] == constants.CMDID_SEND_MESSAGE: rx_data = decode_bridged_message(rx_data) if not rx_data: # the forwarded reply is expected in the next packet continue received = rx_filter(header, rx_data) if not received: self._q.put(rx_data) return rx_data[6:-1] def send_and_receive_raw(self, target, lun, netfn, raw_bytes): """Interface function to send and receive raw message. target: IPMI target lun: logical unit number netfn: network function raw_bytes: RAW bytes as bytestring Returns the IPMI message response bytestring. """ return self._send_and_receive(target=target, lun=lun, netfn=netfn, cmdid=array('B', raw_bytes)[0], payload=raw_bytes[1:]) def send_and_receive(self, req): """Interface function to send and receive an IPMI message. target: IPMI target req: IPMI message request Returns the IPMI message response. """ rx_data = self._send_and_receive(target=req.target, lun=req.lun, netfn=req.netfn, cmdid=req.cmdid, payload=encode_message(req)) rsp = create_message(req.netfn + 1, req.cmdid, req.group_extension) decode_message(rsp, rx_data) return rsp python-ipmi-0.5.4/pyipmi/ipmitool.py000077500000000000000000000553201436677633700175500ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function import sys import getopt import logging import traceback import pprint from array import array from collections import namedtuple import pyipmi import pyipmi.interfaces from pyipmi.utils import py3_array_tobytes Command = namedtuple('Command', 'name fn') CommandHelp = namedtuple('CommandHelp', 'name arguments help') def _print(s): print(s) def _get_command_function(name): for cmd in COMMANDS: if cmd.name == name: return cmd.fn return None def cmd_bmc_info(ipmi, args): device_id = ipmi.get_device_id() print(''' Device ID: %(device_id)s Device Revision: %(revision)s Firmware Revision: %(fw_revision)s IPMI Version: %(ipmi_version)s Manufacturer ID: %(manufacturer_id)d (0x%(manufacturer_id)04x) Product ID: %(product_id)d (0x%(product_id)04x) Device Available: %(available)d Provides SDRs: %(provides_sdrs)d Additional Device Support: '''[1:-1] % device_id.__dict__) functions = ( ('SENSOR', 'Sensor Device'), ('SDR_REPOSITORY', 'SDR Repository Device'), ('SEL', 'SEL Device'), ('FRU_INVENTORY', 'FRU Inventory Device'), ('IPMB_EVENT_RECEIVER', 'IPMB Event Receiver'), ('IPMB_EVENT_GENERATOR', 'IPMB Event Generator'), ('BRIDGE', 'Bridge'), ('CHASSIS', 'Chassis Device') ) for n, s in functions: if device_id.supports_function(n): print(' %s' % s) if device_id.aux is not None: print('Aux Firmware Rev Info: [{:s}]'.format( ' '.join('%02x' % d for d in device_id.aux))) def cmd_sel_clear(ipmi, args): ipmi.clear_sel() def cmd_sensor_rearm(ipmi, args): if len(args) < 1: return number = int(args[0], 0) ipmi.rearm_sensor_events(number) def sdr_show(ipmi, s): print("SDR record ID: 0x%04x" % s.id) print("SDR type: 0x%02x" % s.type) print("Device Id string: %s" % s.device_id_string) print("Entity: %s.%s" % (s.entity_id, s.entity_instance)) if s.type is pyipmi.sdr.SDR_TYPE_FULL_SENSOR_RECORD: (raw, states) = ipmi.get_sensor_reading(s.number, s.owner_lun) value = s.convert_sensor_raw_to_value(raw) if value is None: value = "na" t_unr = s.convert_sensor_raw_to_value(s.threshold['unr']) t_ucr = s.convert_sensor_raw_to_value(s.threshold['ucr']) t_unc = s.convert_sensor_raw_to_value(s.threshold['unc']) t_lnc = s.convert_sensor_raw_to_value(s.threshold['lnc']) t_lcr = s.convert_sensor_raw_to_value(s.threshold['lcr']) t_lnr = s.convert_sensor_raw_to_value(s.threshold['lnr']) print("Reading value: %s" % value) print("Reading state: 0x%x" % states) print("UNR: %s" % t_unr) print("UCR: %s" % t_ucr) print("UNC: %s" % t_unc) print("LNC: %s" % t_lnc) print("LCR: %s" % t_lcr) print("LNR: %s" % t_lnr) elif s.type is pyipmi.sdr.SDR_TYPE_COMPACT_SENSOR_RECORD: (raw, states) = ipmi.get_sensor_reading(s.number) print("Reading: %s" % raw) print("Reading state: 0x%x" % states) def cmd_sdr_show_raw(ipmi, args): if len(args) != 1: usage() return try: sdr = ipmi.get_device_sdr(int(args[0], 0)) print(' '.join(['0x%02x' % b for b in sdr.data])) except ValueError: print('') def cmd_sdr_show(ipmi, args): if len(args) != 1: usage() return try: s = ipmi.get_device_sdr(int(args[0], 0)) sdr_show(ipmi, s) except ValueError: print('') def cmd_sdr_show_all(ipmi, args): for s in ipmi.device_sdr_entries(): try: sdr_show(ipmi, s) except ValueError: print('') print("\n") def print_sdr_list_entry(record_id, number, id_string, value, states): if number: number = str(number) else: number = 'na' if states: states = hex(states) else: states = 'na' print("0x%04x | %3s | %-18s | %9s | %s" % (record_id, number, id_string, value, states)) def cmd_sdr_list(ipmi, args): iter_fct = None device_id = ipmi.get_device_id() if device_id.supports_function('sdr_repository'): iter_fct = ipmi.sdr_repository_entries elif device_id.supports_function('sensor'): iter_fct = ipmi.device_sdr_entries print("SDR-ID | | Device String |") print("=======|=====|====================|====================") for s in iter_fct(): try: number = None value = None states = None if s.type is pyipmi.sdr.SDR_TYPE_FULL_SENSOR_RECORD: (value, states) = ipmi.get_sensor_reading(s.number) number = s.number if value is not None: value = s.convert_sensor_raw_to_value(value) elif s.type is pyipmi.sdr.SDR_TYPE_COMPACT_SENSOR_RECORD: (value, states) = ipmi.get_sensor_reading(s.number) number = s.number id_string = getattr(s, 'device_id_string', None) print_sdr_list_entry(s.id, number, id_string, value, states) except pyipmi.errors.CompletionCodeError as e: if s.type in (pyipmi.sdr.SDR_TYPE_COMPACT_SENSOR_RECORD, pyipmi.sdr.SDR_TYPE_FULL_SENSOR_RECORD): print('0x{:04x} | {:3d} | {:18s} | ERR: CC=0x{:02x}'.format( s.id, s.number, s.device_id_string, e.cc)) def cmd_fru_print(ipmi, args): fru_id = 0 print_all = False if len(args) > 0: fru_id = int(args[0]) if len(args) > 1 and args[1] == 'all': print_all = True inv = ipmi.get_fru_inventory(fru_id) # Chassis Info Area area = inv.chassis_info_area if area: print(''' Chassis Info Area: Type: %(type)d Part Number: %(part_number)s Serial Number: %(serial_number)s '''[1:-1] % area.__dict__) if len(area.custom_chassis_info) != 0: print(' Custom Chassis Info Records:') for field in area.custom_chassis_info: print(' %s' % field) # Board Info Area area = inv.board_info_area if area: print(''' Board Info Area: Mfg. Date / Time: %(mfg_date)s Manufacturer: %(manufacturer)s Product Name: %(product_name)s Serial Number: %(serial_number)s Part Number: %(part_number)s FRU File ID: %(fru_file_id)s '''[1:-1] % area.__dict__) if len(area.custom_mfg_info) != 0: print(' Custom Board Info Records:') for field in area.custom_mfg_info: print(' %s' % field) # Product Info Area area = inv.product_info_area if area: print(''' Product Info Area: Manufacturer: %(manufacturer)s Name: %(name)s Part/Model Number: %(part_number)s Version: %(version)s Serial Number: %(serial_number)s Asset: %(asset_tag)s FRU File ID: %(fru_file_id)s '''[1:-1] % area.__dict__) if len(area.custom_mfg_info) != 0: print(' Custom Board Info Records:') for field in area.custom_mfg_info: print(' %s' % field) # Multirecords area = inv.multirecord_area if area: print('Multirecord Area:') if print_all: for record in area.records: print(' %s' % record) else: print(' Skipped. Use "print all"') def cmd_raw(ipmi, args): lun = 0 if len(args) > 1 and args[0] == 'lun': lun = int(args[1], 0) args = args[2:] if len(args) < 2: usage() return netfn = int(args[0], 0) raw_bytes = array('B', [int(d, 0) for d in args[1:]]) rsp = ipmi.raw_command(lun, netfn, py3_array_tobytes(raw_bytes)) print(' '.join('%02x' % d for d in array('B', rsp))) def cmd_hpm_capabilities(ipmi, args): cap = ipmi.get_target_upgrade_capabilities() for c in cap.components: properties = ipmi.get_component_properties(c) print("Component ID: %d" % c) for prop in properties: print(" %s" % prop) def cmd_hpm_check_file(ipmi, args): if len(args) < 1: return cap = ipmi.open_upgrade_image(args[0]) print(cap.header) for action in cap.actions: print(action) def cmd_hpm_install(ipmi, args): if len(args) < 2: print('missing argument') return ipmi.install_component_from_file(args[0], int(args[1])) def cmd_chassis_status(ipmi, args): status = ipmi.get_chassis_status() print(''' Power ON: %(power_on)s Overload: %(overload)s Interlock: %(interlock)s Fault: %(fault)s Ctrl Fault: %(control_fault)s Restore Policy: %(restore_policy)s '''[1:-1] % status.__dict__) for event in status.last_event: print(event) for state in status.chassis_state: print(state) def cmd_picmg_get_power(ipmi, args): pwr = ipmi.get_power_level(0, 0) print(pwr) def print_link_state(p, s): intf_str = pyipmi.picmg.LinkDescriptor().get_interface_string(p.interface) link_str = pyipmi.picmg.LinkDescriptor().get_link_type_string( p.type, p.extension, p.sig_class) print('CH=%02d INTF=%d FLAGS=0x%x TYPE=%d SIG=%d EXT=%d STATE=%d (%s/%s)' % (p.channel, p.interface, p.link_flags, p.type, p.sig_class, p.extension, s, intf_str, link_str)) def cmd_picmg_get_portstate_all(ipmi, args): for interface in range(3): for channel in range(16): try: (p, s) = ipmi.get_port_state(channel, interface) print_link_state(p, s) except pyipmi.errors.CompletionCodeError as e: if e.cc == 0xcc: continue def cmd_picmg_get_portstate(ipmi, args): if len(args) < 2: return channel = int(args[0]) interface = int(args[1]) (p, s) = ipmi.get_port_state(channel, interface) print_link_state(p, s) def cmd_picmg_getpower_channel_status(ipmi, args): ret = ipmi.get_power_channel_status(int(args[0])) pprint.pprint(vars(ret)) def cmd_picmg_frucontrol_cold_reset(ipmi, args): ipmi.fru_control_cold_reset(0) def cmd_picmg_send_pm_heartbeat(ipmi, args): ipmi.send_pm_heartbeat() def cmd_picmg_send_channel_power(ipmi, args): ipmi.send_channel_power(int(args[0])) def usage(toplevel=False): commands = [] maxlen = 0 if toplevel: argv = [] else: argv = sys.argv[1:] # (1) try to find help for commands on exactly one level above for cmd in COMMAND_HELP: subcommands = cmd.name.split(' ') if (len(subcommands) == len(argv) + 1 and subcommands[:len(argv)] == argv): commands.append(cmd) if cmd.arguments: maxlen = max(maxlen, len(cmd.name)+len(cmd.arguments)+1) else: maxlen = max(maxlen, len(cmd.name)) # (2) if nothing found, try to find help on any level above if maxlen == 0: for cmd in COMMAND_HELP: subcommands = cmd.name.split(' ') if (len(subcommands) > len(argv) + 1 and subcommands[:len(argv)] == argv): commands.append(cmd) if cmd.arguments: maxlen = max(maxlen, len(cmd.name)+len(cmd.arguments)+1) else: maxlen = max(maxlen, len(cmd.name)) # (3) find help on same level if maxlen == 0: for cmd in COMMAND_HELP: subcommands = cmd.name.split(' ') if (len(subcommands) == len(argv) and subcommands[:len(argv)] == argv): commands.append(cmd) if cmd.arguments: maxlen = max(maxlen, len(cmd.name)+len(cmd.arguments)+1) else: maxlen = max(maxlen, len(cmd.name)) # if still nothing found, print toplevel usage if maxlen == 0: usage(toplevel=True) return if len(argv) == 0: version() print('usage: ipmitool [options...] ') print(''' Options: -t Set target IPMB address -b Set target channel -r Set target routing (not supported atm) -h Show this help -v Be verbose -V Print version -I Set interface (available: rmcp, aardvark, ipmitool, ipmbdev) -H Set RMCP host -U Set RMCP user -P Set RMCP password -o Set interface specific options (name=value, separated by commas, see below for available options). '''[1:]) print(''' Aardvark interface options: serial= Serial number of the device pullups= Enable/disable pullups power= Enable/disable target power Ipmitool interface options: interface_type Set the interface type to be used (lan, lanplus, serial, open) Ipmbdev interface options: port= Specify path to Linux IPMB device (/dev/ipmb-0 by default) '''[1:]) print('Commands:') for cmd in commands: name = cmd.name if cmd.arguments: name = '%s %s' % (name, cmd.arguments) print(' %-*s %s' % (maxlen, name, cmd.help)) def version(): print('ipmitool v%s' % pyipmi.__version__) def parse_interface_options(interface_name, options): if options: options = options.split(',') interface_options = {} for option in options: (name, value) = option.split('=', 1) if interface_name == 'aardvark': if name == 'serial': interface_options['serial_number'] = value elif (name, value) == ('pullups', 'on'): interface_options['enable_i2c_pullups'] = True elif (name, value) == ('pullups', 'off'): interface_options['enable_i2c_pullups'] = False elif (name, value) == ('power', 'on'): interface_options['enable_target_power'] = True elif (name, value) == ('power', 'off'): interface_options['enable_target_power'] = False else: print('Warning: unknown option %s' % name) elif interface_name == 'ipmitool': if name == 'interface_type': interface_options['interface_type'] = value else: print('Warning: unknown option %s' % name) elif interface_name == 'ipmbdev': if name == 'port': interface_options['port'] = value return interface_options def main(): try: opts, args = getopt.getopt(sys.argv[1:], 't:hvVI:H:U:P:o:b:p:r:') except getopt.GetoptError as err: print(str(err)) usage() sys.exit(2) verbose = False interface_name = 'aardvark' target_address = 0x20 target_routing = None rmcp_host = None rmcp_port = 623 rmcp_user = '' rmcp_password = '' interface_options = list() for o, a in opts: if o == '-v': verbose = True elif o == '-h': usage() sys.exit() elif o == '-V': version() sys.exit() elif o == '-t': target_address = int(a, 0) elif o == '-b': target_routing = [(0x20, int(a), 0)] elif o == '-r': target_routing = a elif o == '-H': rmcp_host = a elif o == '-p': rmcp_port = int(a, 0) elif o == '-U': rmcp_user = a elif o == '-P': rmcp_password = a elif o == '-I': interface_name = a elif o == '-o': interface_options = a else: assert False, 'unhandled option' # fake sys.argv sys.argv = [sys.argv[0]] + args if len(args) == 0: usage() sys.exit(1) handler = logging.StreamHandler() if verbose: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.INFO) pyipmi.logger.add_log_handler(handler) pyipmi.logger.set_log_level(logging.DEBUG) for i in range(len(args)): cmd = _get_command_function(' '.join(args[0:i+1])) if cmd is not None: args = args[i+1:] break else: usage() sys.exit(1) interface_options = parse_interface_options(interface_name, interface_options) try: interface = pyipmi.interfaces.create_interface(interface_name, **interface_options) except RuntimeError as e: print(e) sys.exit(1) ipmi = pyipmi.create_connection(interface) ipmi.target = pyipmi.Target(target_address) if target_routing is not None: ipmi.target.set_routing(target_routing) if rmcp_host is not None: ipmi.session.set_session_type_rmcp(rmcp_host, rmcp_port) ipmi.session.set_auth_type_user(rmcp_user, rmcp_password) ipmi.session.establish() try: cmd(ipmi, args) except pyipmi.errors.CompletionCodeError as e: print('Command returned with completion code 0x%02x' % e.cc) if verbose: traceback.print_exc() sys.exit(1) except pyipmi.errors.IpmiTimeoutError: print('Command timed out') if verbose: traceback.print_exc() sys.exit(1) except KeyboardInterrupt: if verbose: traceback.print_exc() sys.exit(1) finally: if rmcp_host is not None: ipmi.session.close() COMMANDS = ( Command('bmc info', cmd_bmc_info), Command('bmc reset cold', lambda i, a: i.cold_reset()), Command('bmc reset warm', lambda i, a: i.warm_reset()), Command('sel list', lambda i, a: list(map(_print, i.sel_entries()))), Command('sel clear', cmd_sel_clear), Command('sensor rearm', cmd_sensor_rearm), Command('sdr list', cmd_sdr_list), Command('sdr raw', cmd_sdr_show_raw), Command('sdr show', cmd_sdr_show), Command('sdr showall', cmd_sdr_show_all), Command('fru print', cmd_fru_print), Command('picmg frucontrol cr', cmd_picmg_frucontrol_cold_reset), Command('picmg power get', cmd_picmg_get_power), Command('picmg portstate get', cmd_picmg_get_portstate), Command('picmg portstate getall', cmd_picmg_get_portstate_all), Command('picmg channel status', cmd_picmg_getpower_channel_status), Command('picmg send heartbeat', cmd_picmg_send_pm_heartbeat), Command('picmg channel power', cmd_picmg_send_channel_power), Command('raw', cmd_raw), Command('hpm capabilities', cmd_hpm_capabilities), Command('hpm check', cmd_hpm_check_file), Command('hpm install', cmd_hpm_install), Command('chassis status', cmd_chassis_status), Command('chassis power off', lambda i, a: i.chassis_control_power_down()), Command('chassis power on', lambda i, a: i.chassis_control_power_up()), Command('chassis power cycle', lambda i, a: i.chassis_control_power_cycle()), Command('chassis power reset', lambda i, a: i.chassis_control_hard_reset()), Command('chassis power diag', lambda i, a: i.chassis_control_power_diagnostic_interrupt()), Command('chassis power soft', lambda i, a: i.chassis_control_power_soft_shutdown()), ) COMMAND_HELP = ( CommandHelp('raw', None, 'Send a RAW IPMI request and print response'), CommandHelp('fru', None, 'Print built-in FRU and scan SDR for FRU locators'), CommandHelp('sensor', None, None), CommandHelp('sensor rearm', '', 'Rearm Sensor Events'), CommandHelp('sel', None, 'Print System Event Log (SEL)'), CommandHelp('sel list', None, 'List all SEL entries'), CommandHelp('sel clear', None, 'Clear SEL'), CommandHelp('sdr', None, 'Print Sensor Data Repository entries and readings'), CommandHelp('sdr list', None, 'List all SDRs'), CommandHelp('sdr raw', '', 'Show SDR raw data'), CommandHelp('sdr show', '', 'Show detail for one SDR'), CommandHelp('sdr showall', None, 'Show detail for all SDRs'), CommandHelp('bmc', None, 'Management Controller status and global enables'), CommandHelp('bmc info', None, 'BMC Device ID inforamtion'), CommandHelp('bmc reset', '', 'BMC reset control'), CommandHelp('picmg', None, 'PICMG commands'), CommandHelp('picmg frucontrol', '', 'Issue frucontrol'), CommandHelp('picmg power get', 'get PICMG power level', 'Request the power level'), CommandHelp('picmg portstate getall', '', 'Request all portstates for all interfaces'), CommandHelp('picmg portstate get', ' ', 'Request the portstate for an interface'), CommandHelp('hpm', None, 'HPM.1 commands'), CommandHelp('hpm capabilities', 'HPM.1 target upgrade capabilities', 'Request the target upgrade capabilities'), CommandHelp('hpm check', 'HPM.1 file check', 'Check the specified HPM.1 file'), CommandHelp('hpm install', ' ', 'Install the specified HPM.1 file to the controller'), CommandHelp('chassis', None, 'Get chassis status and set power state'), CommandHelp('chassis status', '', 'Get chassis status'), CommandHelp('chassis power', '', 'Set power state') ) if __name__ == '__main__': main() python-ipmi-0.5.4/pyipmi/lan.py000066400000000000000000000223171436677633700164630ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .msgs import create_request_by_name from .utils import check_completion_code, ByteBuffer LAN_PARAMETER_SET_IN_PROGRESS = 0 LAN_PARAMETER_AUTHENTICATION_TYPE_SUPPORT = 1 LAN_PARAMETER_AUTHENTICATION_TYPE_ENABLE = 2 LAN_PARAMETER_IP_ADDRESS = 3 LAN_PARAMETER_IP_ADDRESS_SOURCE = 4 LAN_PARAMETER_MAC_ADDRESS = 5 LAN_PARAMETER_SUBNET_MASK = 6 LAN_PARAMETER_IPV4_HEADER_PARAMETERS = 7 LAN_PARAMETER_PRIMARY_RMCP_PORT = 8 LAN_PARAMETER_SECONDARY_RMCP_PORT = 9 LAN_PARAMETER_BMC_GENERATED_ARP_CONTROL = 10 LAN_PARAMETER_GRATUITOUS_ARP_INTERVAL = 11 LAN_PARAMETER_DEFAULT_GATEWAY_ADDRESS = 12 LAN_PARAMETER_DEFAULT_GATEWAY_MAC_ADDRESS = 13 LAN_PARAMETER_BACKUP_GATEWAY_ADDRESS = 14 LAN_PARAMETER_BACKUP_GATEWAY_MAC_ADDRESS = 15 LAN_PARAMETER_COMMUNITY_STRING = 16 LAN_PARAMETER_NUMBER_OF_DESTINATIONS = 17 LAN_PARAMETER_DESTINATION_TYPE = 18 LAN_PARAMETER_DESTINATION_ADDRESSES = 19 # following parameters are introduced with IPMI v2.0/RMCP+ LAN_PARAMETER_802_1Q_VLAN_ID = 20 LAN_PARAMETER_802_1Q_VLAN_PRIORITY = 21 LAN_PARAMETER_RMCP_PLUS_MESSAGING_CIPHER_SUITE_ENTRY_SUPPORT = 22 LAN_PARAMETER_RMCP_PLUS__MESSAGING_CIPHER_SUITE_ENTRIES = 23 LAN_PARAMETER_RMCP_PLUS_MESSAGING_CIPHER_SUITE_PRIVILEGE_LEVES = 24 LAN_PARAMETER_DESTINATION_ADDRESS_VLAN_TAGS = 25 LAN_PARAMETER_IP_ADDRESS_SOURCE_UNSPECIFIED = 0 LAN_PARAMETER_IP_ADDRESS_SOURCE_STATIC = 1 LAN_PARAMETER_IP_ADDRESS_SOURCE_DHCP = 2 LAN_PARAMETER_IP_ADDRESS_SOURCE_BIOS_OR_SYSTEM_SOFTWARE = 3 LAN_PARAMETER_IP_ADDRESS_SOURCE_BMC_OTHER_PROTOCOL = 4 CONVERT_RAW_TO_IP_SRC = { 0: "unknown", 1: "static", 2: "dhcp", 3: "bios", 4: "other" } def data_to_ip_address(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS)` response data into the string representation of the encoded ip address, in format xxx.xxx.xxx.xxx . """ return '.'.join(map(str, data)) def ip_address_to_data(ip_address): """ Convert an ip address (string) into a `SetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS)` request data. """ return ByteBuffer(map(int, ip_address.split('.'))) def data_to_ip_source(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS_SOURCE)` response data into the string representation of the encoded ip source. """ # The ip source is encoded in the last 4 bits of the response return CONVERT_RAW_TO_IP_SRC[data[0] & 0b1111] def ip_source_to_data(ip_source): """ Convert an ip source (string) into a `SetLanConfigurationParameters(LAN_PARAMETER_IP_ADDRESS_SOURCE)` request data. """ if ip_source == "dhcp": data = ByteBuffer([2]) elif ip_source == "static": data = ByteBuffer([1]) else: raise ValueError(f"Unknown value for ip_source argument: {ip_source}. Possible values are: dhcp, static.") return data def data_to_mac_address(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_MAC_ADDRESS)` response data into the string representation of the encoded mac address, in format aa:bb:cc:dd:ee:ff . """ return ':'.join([f"{i:02x}" for i in data]) def data_to_vlan(data): """ Convert a `GetLanConfigurationParameters(LAN_PARAMETER_802_1Q_VLAN_ID)` response data into an integer representation of the encoded vlan. """ # The vlan ID must be extracted from the response, according to IPMI # specification. # # Example with VLAN ID = 394 # The raw response to `get_lan_config_param` will be [138, 129] # The binary representation will be : # # | data[0] | | data[1] | # Rsp : 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 # ^ ^ ^ ^ # |--------------| |-----| # |least sign. bits |most sign. bits # # By rearranging the bits order, we get the VLAN value : # 0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 0 = 394 return ((data[1] & 0b1111) << 8) | data[0] def vlan_to_data(vlan): """ Convert a vlan (int) into a `SetLanConfigurationParameters(LAN_PARAMETER_802_1Q_VLAN_ID)` request data. """ if not isinstance(vlan, int): raise TypeError(f"Wrong type for vlan argument: {type(vlan)}, expected int.") elif vlan > 4095: raise ValueError(f"Wrong value for vlan argument: {vlan}. It cannot be greater than 4095.") if vlan == 0: # We want to deactivate the vlan (no vlan) data = ByteBuffer([0, 0]) else: # We want to set the vlan ID # first separate the two bytes of the vlan least_sign_byte = vlan & 0b11111111 most_sign_byte = (vlan >> 8) & 0b1111 # then create the vlan enabled bit vlan_enable = 0b10000000 # finally we concatenate every byte together data = ByteBuffer([least_sign_byte, vlan_enable | most_sign_byte]) return data class Lan(object): def get_lan_config_param(self, channel=0, parameter_selector=0, set_selector=0, block_selector=0, revision_only=0): req = create_request_by_name('GetLanConfigurationParameters') req.command.get_parameter_revision_only = revision_only if revision_only != 1: req.command.channel_number = channel req.parameter_selector = parameter_selector req.set_selector = set_selector req.block_selector = block_selector rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.data def set_lan_config_param(self, channel, parameter_selector, data): req = create_request_by_name('SetLanConfigurationParameters') req.command.channel_number = channel req.parameter_selector = parameter_selector req.data = data rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_ip_address(self, channel=0): """ Return a string representing the ip address of the device, in format xxx.xxx.xxx.xxx """ ip_address_raw = self.get_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS) return data_to_ip_address(ip_address_raw) def set_ip_address(self, ip_address, channel=0): """ WARNING: changing the IP address of the BMC will make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = ip_address_to_data(ip_address) self.set_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS, data) def get_ip_source(self, channel=0): """ Return a string representing the ip source of the device. Possible values are listed in `CONVERT_RAW_TO_IP_SRC` variable. """ ip_source_raw = self.get_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS_SOURCE) return data_to_ip_source(ip_source_raw) def set_ip_source(self, ip_source, channel=0): """ WARNING: changing the IP source may change the IP address of the BMC, which will make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = ip_source_to_data(ip_source) self.set_lan_config_param(channel, LAN_PARAMETER_IP_ADDRESS_SOURCE, data) def get_mac_address(self, channel=0): """ Return a string representing the mac address of the device, in format aa:bb:cc:dd:ee:ff. """ mac_address_raw = self.get_lan_config_param(channel, LAN_PARAMETER_MAC_ADDRESS) return data_to_mac_address(mac_address_raw) def get_vlan_id(self, channel=0): """ Return the 802.1q VLAN ID of the device. """ vlan_id_raw = self.get_lan_config_param(channel, LAN_PARAMETER_802_1Q_VLAN_ID) return data_to_vlan(vlan_id_raw) def set_vlan_id(self, vlan, channel=0): """ WARNING: changing the VLAN ID may change the IP address of the BMC depending on your current network configuration. This could make a current lan session unusable because it still has the former IP address. Be sure to open a new session with the new IP for future calls if using a lan interface. """ data = vlan_to_data(vlan) self.set_lan_config_param(channel, LAN_PARAMETER_802_1Q_VLAN_ID, data) class LanParameter(object): pass python-ipmi-0.5.4/pyipmi/logger.py000066400000000000000000000020531436677633700171630ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import logging def log(): return logging.getLogger('pyipmi') def add_log_handler(handler): log().addHandler(handler) def set_log_level(level): log().setLevel(level) class NullHandler(logging.Handler): def emit(self, record): pass add_log_handler(NullHandler()) python-ipmi-0.5.4/pyipmi/messaging.py000066400000000000000000000161751436677633700176730ustar00rootroot00000000000000# Copyright (c) 2016 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from enum import Enum from .session import Session from .msgs import create_request_by_name from .utils import check_completion_code from .state import State class PasswordOperation(int, Enum): DISABLE = 0b00 ENABLE = 0b01 SET_PASSWORD = 0b10 TEST_PASSWORD = 0b11 class UserPrivilegeLevel(str, Enum): RESERVED = "reserved" CALLBACK = "callback" USER = "user" OPERATOR = "operator" ADMINISTRATOR = "administrator" OEM = "oem" NO_ACCESS = "no access" CONVERT_RAW_TO_USER_PRIVILEGE = { 0x00: UserPrivilegeLevel.RESERVED, 0x01: UserPrivilegeLevel.CALLBACK, 0x02: UserPrivilegeLevel.USER, 0x03: UserPrivilegeLevel.OPERATOR, 0x04: UserPrivilegeLevel.ADMINISTRATOR, 0x05: UserPrivilegeLevel.OEM, 0x0F: UserPrivilegeLevel.NO_ACCESS } CONVERT_USER_PRIVILEGE_TO_RAW = { UserPrivilegeLevel.RESERVED: 0x00, UserPrivilegeLevel.CALLBACK: 0x01, UserPrivilegeLevel.USER: 0x02, UserPrivilegeLevel.OPERATOR: 0x03, UserPrivilegeLevel.ADMINISTRATOR: 0x04, UserPrivilegeLevel.OEM: 0x05, UserPrivilegeLevel.NO_ACCESS: 0x0F } class Messaging(object): def get_channel_authentication_capabilities(self, channel, priv_lvl): req = create_request_by_name('GetChannelAuthenticationCapabilities') req.channel.number = channel req.privilege_level.requested = priv_lvl rsp = self.send_and_receive(req) check_completion_code(rsp.completion_code) caps = ChannelAuthenticationCapabilities(rsp) return caps def set_username(self, userid=0, username=''): req = create_request_by_name('SetUserName') req.userid.userid = userid req.user_name = username.ljust(16, '\x00') rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_username(self, userid=0): req = create_request_by_name('GetUserName') req.userid.userid = userid rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.user_name def get_user_access(self, userid=0, channel=0): req = create_request_by_name('GetUserAccess') req.userid.userid = userid req.channel.channel_number = channel rsp = self.send_message(req) check_completion_code(rsp.completion_code) return UserAccess(rsp) def set_user_access(self, userid, ipmi_msg, link_auth, callback_only, priv_level, channel=0, enable_change=1): req = create_request_by_name('SetUserAccess') req.channel_access.channel_number = channel req.channel_access.ipmi_msg = ipmi_msg req.channel_access.link_auth = link_auth req.channel_access.callback = callback_only req.channel_access.enable_change = enable_change req.userid.userid = userid req.privilege.privilege_level = CONVERT_USER_PRIVILEGE_TO_RAW.get( priv_level, 0x0F) rsp = self.send_message(req) check_completion_code(rsp.completion_code) def set_user_password(self, userid, password=''): if len(password) > 16: raise ValueError("Password length cannot be greater than 20.") req = create_request_by_name('SetUserPassword') req.userid.userid = userid req.operation.operation = PasswordOperation.SET_PASSWORD req.password = password.ljust(16, '\x00') rsp = self.send_message(req) check_completion_code(rsp.completion_code) def enable_user(self, userid): req = create_request_by_name('SetUserPassword') req.userid.userid = userid req.operation.operation = PasswordOperation.ENABLE rsp = self.send_message(req) check_completion_code(rsp.completion_code) def disable_user(self, userid): req = create_request_by_name('SetUserPassword') req.userid.userid = userid req.operation.operation = PasswordOperation.DISABLE rsp = self.send_message(req) check_completion_code(rsp.completion_code) class ChannelAuthenticationCapabilities(State): _functions = { 'none': Session.AUTH_TYPE_NONE, 'md2': Session.AUTH_TYPE_MD2, 'md5': Session.AUTH_TYPE_MD5, 'straight': Session.AUTH_TYPE_PASSWORD, 'oem_proprietary': Session.AUTH_TYPE_OEM, } def _from_response(self, rsp): self.channel = rsp.channel_number self.auth_types = [] self.ipmi_1_5 = False self.ipmi_2_0 = False if rsp.support.ipmi_2_0: self.ipmi_2_0 = True else: self.ipmi_1_5 = True for function in self._functions.keys(): if hasattr(rsp.support, function): if getattr(rsp.support, function): self.auth_types.append(function) def get_max_auth_type(self): for auth_type in ('md5', 'md2', 'straight', 'oem_proprietary', 'none'): if auth_type in self.auth_types: return self._functions[auth_type] return None def __str__(self): s = 'Authentication Capabilities:\n' s += ' IPMI v1.5: %s\n' % self.ipmi_1_5 s += ' IPMI v2.0: %s\n' % self.ipmi_2_0 s += ' Auth. types: %s\n' % ' '.join(self.auth_types) s += ' Max Auth. type: %s\n' % self.get_max_auth_type() return s class UserAccess(State): def _from_response(self, rsp): self.user_count = rsp.max_user.max_user self.enabled_user_count = rsp.enabled_user.count self.enabled_status = rsp.enabled_user.status self.fixed_name_user_count = rsp.fixed_names.count self.privilege_level = CONVERT_RAW_TO_USER_PRIVILEGE.get(rsp.channel_access.privilege, UserPrivilegeLevel.RESERVED) self.ipmi_messaging = rsp.channel_access.ipmi_msg == 1 self.link_auth = rsp.channel_access.link_auth == 1 self.callback_only = rsp.channel_access.callback == 1 def __str__(self): s = 'User Access:\n' s += ' Max user number: %i\n' % self.user_count s += ' Enabled user: %i\n' % self.enabled_user_count s += ' Enabled status: %i\n' % self.enabled_status s += ' Fixed name user: %i\n' % self.fixed_name_user_count s += ' Privilege level: %s\n' % self.privilege_level s += ' IPMI messaging: %s\n' % self.ipmi_messaging s += ' Link Auth.: %s\n' % self.link_auth s += ' Callback only: %s' % self.callback_only python-ipmi-0.5.4/pyipmi/msgs/000077500000000000000000000000001436677633700163035ustar00rootroot00000000000000python-ipmi-0.5.4/pyipmi/msgs/__init__.py000066400000000000000000000044361436677633700204230ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .registry import register_message_class # noqa:F401 from .registry import create_request_by_name # noqa:F401 from .registry import create_response_by_name # noqa:F401 from .registry import create_message # noqa:F401 from .registry import create_response_message # noqa:F401 from .message import Message # noqa:F401 from .message import ByteArray # noqa:F401 from .message import VariableByteArray # noqa:F401 from .message import UnsignedInt # noqa:F401 from .message import UnsignedIntMask # noqa:F401 from .message import Timestamp # noqa:F401 from .message import Bitfield # noqa:F401 from .message import CompletionCode # noqa:F401 from .message import Conditional # noqa:F401 from .message import Optional # noqa:F401 from .message import RemainingBytes # noqa:F401 from .message import String # noqa:F401 from .message import EventMessageRevision # noqa:F401 from .message import GroupExtensionIdentifier # noqa:F401 from .message import encode_message # noqa:F401 from .message import decode_message # noqa:F401 from .message import pack_message # noqa:F401 from . import bmc # noqa:F401 from . import chassis # noqa:F401 from . import dcmi # noqa:F401 from . import device_messaging # noqa:F401 from . import fru # noqa:F401 from . import hpm # noqa:F401 from . import picmg # noqa:F401 from . import sdr # noqa:F401 from . import sel # noqa:F401 from . import sensor # noqa:F401 from . import event # noqa:F401 from . import lan # noqa:F401 from . import vita # noqa:F401 python-ipmi-0.5.4/pyipmi/msgs/bmc.py000066400000000000000000000176511436677633700174300ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from . import constants from . import register_message_class from . import Message from . import ByteArray from . import UnsignedInt from . import Bitfield from . import CompletionCode from . import Optional from . import RemainingBytes SELFTEST_RESULT_NO_ERROR = 0x55 SELFTEST_RESULT_NOT_IMPLEMENTED = 0x56 SELFTEST_RESULT_CORRUPTED_DATA_OR_INACCESSIBLE_DEVICE = 0x57 SELFTEST_RESULT_FATAL_HARDWARE_ERROR = 0x58 @register_message_class class GetDeviceIdReq(Message): __cmdid__ = constants.CMDID_GET_DEVICE_ID __netfn__ = constants.NETFN_APP @register_message_class class GetDeviceIdRsp(Message): __cmdid__ = constants.CMDID_GET_DEVICE_ID __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), UnsignedInt('device_id', 1), Bitfield('device_revision', 1, Bitfield.Bit('device_revision', 4), Bitfield.ReservedBit(3, 0), Bitfield.Bit('provides_device_sdrs', 1)), Bitfield('firmware_revision', 2, Bitfield.Bit('major', 7), Bitfield.Bit('device_available', 1), Bitfield.Bit('minor', 8)), UnsignedInt('ipmi_version', 1), Bitfield('additional_support', 1, Bitfield.Bit('sensor', 1), Bitfield.Bit('sdr_repository', 1), Bitfield.Bit('sel', 1), Bitfield.Bit('fru_inventory', 1), Bitfield.Bit('ipmb_event_receiver', 1), Bitfield.Bit('ipmb_event_generator', 1), Bitfield.Bit('bridge', 1), Bitfield.Bit('chassis', 1)), UnsignedInt('manufacturer_id', 3), UnsignedInt('product_id', 2), Optional(ByteArray('auxiliary', 4)) ) @register_message_class class ColdResetReq(Message): __cmdid__ = constants.CMDID_COLD_RESET __netfn__ = constants.NETFN_APP @register_message_class class ColdResetRsp(Message): __cmdid__ = constants.CMDID_COLD_RESET __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class WarmResetReq(Message): __cmdid__ = constants.CMDID_WARM_RESET __netfn__ = constants.NETFN_APP @register_message_class class WarmResetRsp(Message): __cmdid__ = constants.CMDID_WARM_RESET __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class ManufacturingTestOnReq(Message): __cmdid__ = constants.CMDID_MANUFACTURING_TEST_ON __netfn__ = constants.NETFN_APP __fields__ = ( RemainingBytes('data'), ) @register_message_class class ManufacturingTestOnRsp(Message): __cmdid__ = constants.CMDID_MANUFACTURING_TEST_ON __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), RemainingBytes('data'), ) @register_message_class class GetSelftestResultsReq(Message): __cmdid__ = constants.CMDID_GET_SELF_TEST_RESULTS __netfn__ = constants.NETFN_APP @register_message_class class GetSelftestResultsRsp(Message): __cmdid__ = constants.CMDID_GET_SELF_TEST_RESULTS __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), UnsignedInt('result', 1), Bitfield('status', 1, Bitfield.Bit('controller_firmware_corrupted', 1, 0), Bitfield.Bit('controller_bootblock_corrupted', 1, 0), Bitfield.Bit('internal_use_area_corrupted', 1, 0), Bitfield.Bit('sdr_repository_empty', 1, 0), Bitfield.Bit('ipmb_signal_lines_do_not_respond', 1, 0), Bitfield.Bit('cannot_access_bmc_fru_device', 1, 0), Bitfield.Bit('cannot_access_sdr_device', 1, 0), Bitfield.Bit('cannot_access_sel_device', 1, 0),), ) @register_message_class class SetAcpiPowerStateReq(Message): __cmdid__ = constants.CMDID_SET_ACPI_POWER_STATE __netfn__ = constants.NETFN_APP __not_implemented__ = True @register_message_class class SetAcpiPowerStateRsp(Message): __cmdid__ = constants.CMDID_SET_ACPI_POWER_STATE __netfn__ = constants.NETFN_APP | 1 __not_implemented__ = True @register_message_class class GetAcpiPowerStateReq(Message): __cmdid__ = constants.CMDID_GET_ACPI_POWER_STATE __netfn__ = constants.NETFN_APP __not_implemented__ = True @register_message_class class GetAcpiPowerStateRsp(Message): __cmdid__ = constants.CMDID_GET_ACPI_POWER_STATE __netfn__ = constants.NETFN_APP | 1 __not_implemented__ = True @register_message_class class GetDeviceGuideReq(Message): __cmdid__ = constants.CMDID_GET_DEVICE_GUID __netfn__ = constants.NETFN_APP __not_implemented__ = True @register_message_class class GetDeviceGuideRsp(Message): __cmdid__ = constants.CMDID_GET_DEVICE_GUID __netfn__ = constants.NETFN_APP | 1 __not_implemented__ = True @register_message_class class ResetWatchdogTimerReq(Message): __cmdid__ = constants.CMDID_RESET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP @register_message_class class ResetWatchdogTimerRsp(Message): __cmdid__ = constants.CMDID_RESET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class SetWatchdogTimerReq(Message): __cmdid__ = constants.CMDID_SET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('timer_use', 1, Bitfield.Bit('timer_use', 3), Bitfield.ReservedBit(3, 0), Bitfield.Bit('dont_stop', 1, 0), Bitfield.Bit('dont_log', 1, 0),), Bitfield('timer_actions', 1, Bitfield.Bit('timeout_action', 3), Bitfield.ReservedBit(1, 0), Bitfield.Bit('pre_timeout_interrupt', 3), Bitfield.ReservedBit(1, 0),), UnsignedInt('pre_timeout_interval', 1), UnsignedInt('timer_use_expiration_flags', 1), UnsignedInt('initial_countdown', 2), ) @register_message_class class SetWatchdogTimerRsp(Message): __cmdid__ = constants.CMDID_SET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetWatchdogTimerReq(Message): __cmdid__ = constants.CMDID_GET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP @register_message_class class GetWatchdogTimerRsp(Message): __cmdid__ = constants.CMDID_GET_WATCHDOG_TIMER __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('timer_use', 1, Bitfield.Bit('timer_use', 3), Bitfield.ReservedBit(3, 0), Bitfield.Bit('is_running', 1, 0), Bitfield.Bit('dont_log', 1, 0),), Bitfield('timer_actions', 1, Bitfield.Bit('timeout_action', 3), Bitfield.ReservedBit(1, 0), Bitfield.Bit('pre_timeout_interrupt', 3), Bitfield.ReservedBit(1, 0),), UnsignedInt('pre_timeout_interval', 1), UnsignedInt('timer_use_expiration_flags', 1), UnsignedInt('initial_countdown', 2), UnsignedInt('present_countdown', 2), ) python-ipmi-0.5.4/pyipmi/msgs/chassis.py000066400000000000000000000143021436677633700203120ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import UnsignedInt from . import Bitfield from . import CompletionCode from . import Optional from . import RemainingBytes CONTROL_POWER_DOWN = 0 CONTROL_POWER_UP = 1 CONTROL_POWER_CYCLE = 2 CONTROL_HARD_RESET = 3 CONTROL_DIAGNOSTIC_INTERRUPT = 4 CONTROL_SOFT_SHUTDOWN = 5 @register_message_class class GetChassisCapabilitiesReq(Message): __cmdid__ = constants.CMDID_GET_CHASSIS_CAPABILITIES __netfn__ = constants.NETFN_CHASSIS @register_message_class class GetChassisCapabilitiesRsp(Message): __cmdid__ = constants.CMDID_GET_CHASSIS_CAPABILITIES __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), Bitfield('capabilities_flags', 1, Bitfield.Bit('intrusion_sensor', 1), Bitfield.Bit('frontpanel_lockout', 1), Bitfield.Bit('diagnostic_interrupt', 1), Bitfield.Bit('power_interlock', 1), Bitfield.ReservedBit(4, 0)), UnsignedInt('fru_info_device_address', 1), UnsignedInt('sdr_device_address', 1), UnsignedInt('sel_device_address', 1), UnsignedInt('system_management_device_address', 1), Optional( UnsignedInt('bridge_device_address', 1) ), ) @register_message_class class GetChassisStatusReq(Message): __cmdid__ = constants.CMDID_GET_CHASSIS_STATUS __netfn__ = constants.NETFN_CHASSIS @register_message_class class GetChassisStatusRsp(Message): __cmdid__ = constants.CMDID_GET_CHASSIS_STATUS __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), Bitfield('current_power_state', 1, Bitfield.Bit('power_on', 1), Bitfield.Bit('power_overload', 1), Bitfield.Bit('interlock', 1), Bitfield.Bit('power_fault', 1), Bitfield.Bit('power_control_fault', 1), Bitfield.Bit('power_restore_policy', 2), Bitfield.ReservedBit(1, 0),), Bitfield('last_power_event', 1, Bitfield.Bit('ac_failed', 1), Bitfield.Bit('power_overload', 1), Bitfield.Bit('power_interlock', 1), Bitfield.Bit('power_fault', 1), Bitfield.Bit('power_is_on_via_ipmi_command', 1), Bitfield.ReservedBit(3, 0),), Bitfield('misc_chassis_state', 1, Bitfield.Bit('chassis_intrusion_active', 1), Bitfield.Bit('front_panel_lockout_active', 1), Bitfield.Bit('drive_fault', 1), Bitfield.Bit('cooling_fault_detected', 1), Bitfield.Bit('chassis_id_state', 2), Bitfield.Bit('id_cmd_state_info_support', 1), Bitfield.ReservedBit(1, 0),), Optional( UnsignedInt('front_panel_button_capabilities', 1), ), ) @register_message_class class ChassisControlReq(Message): __cmdid__ = constants.CMDID_CHASSIS_CONTROL __netfn__ = constants.NETFN_CHASSIS __fields__ = ( Bitfield('control', 1, Bitfield.Bit('option', 4), Bitfield.ReservedBit(4, 0)), ) @register_message_class class ChassisControlRsp(Message): __cmdid__ = constants.CMDID_CHASSIS_CONTROL __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetPohCounterReq(Message): __cmdid__ = constants.CMDID_GET_POH_COUNTER __netfn__ = constants.NETFN_CHASSIS @register_message_class class GetPohCounterRsp(Message): __cmdid__ = constants.CMDID_GET_POH_COUNTER __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), UnsignedInt('minutes_per_count', 1), UnsignedInt('counter_reading', 4), ) @register_message_class class GetSystemBootOptionsReq(Message): __cmdid__ = constants.CMDID_GET_SYSTEM_BOOT_OPTIONS __netfn__ = constants.NETFN_CHASSIS __fields__ = ( Bitfield('parameter_selector', 1, Bitfield.Bit('boot_option_parameter_selector', 7), Bitfield.ReservedBit(1)), UnsignedInt('set_selector', 1, 0), UnsignedInt('block_selector', 1, 0) ) @register_message_class class GetSystemBootOptionsRsp(Message): __cmdid__ = constants.CMDID_GET_SYSTEM_BOOT_OPTIONS __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), Bitfield('parameter_version', 1, Bitfield.Bit('parameter_version', 4, default=1), Bitfield.ReservedBit(4)), Bitfield('parameter_valid', 1, Bitfield.Bit('boot_option_parameter_selector', 7), Bitfield.Bit('parameter_validity', 1)), RemainingBytes('data'), ) @register_message_class class SetSystemBootOptionsReq(Message): __cmdid__ = constants.CMDID_SET_SYSTEM_BOOT_OPTIONS __netfn__ = constants.NETFN_CHASSIS __fields__ = ( Bitfield('parameter_selector', 1, Bitfield.Bit('boot_option_parameter_selector', 7), Bitfield.Bit('parameter_validity', 1, default=0)), RemainingBytes('data') ) @register_message_class class SetSystemBootOptionsRsp(Message): __cmdid__ = constants.CMDID_SET_SYSTEM_BOOT_OPTIONS __netfn__ = constants.NETFN_CHASSIS | 1 __fields__ = ( CompletionCode(), ) python-ipmi-0.5.4/pyipmi/msgs/constants.py000066400000000000000000000332071436677633700206760ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # completion codes CC_OK = 0x00 CC_NODE_BUSY = 0xc0 CC_INV_CMD = 0xc1 CC_INV_CMD_FOR_LUN = 0xc2 CC_TIMEOUT = 0xc3 CC_OUT_OF_SPACE = 0xc4 CC_RES_CANCELED = 0xc5 CC_REQ_DATA_TRUNC = 0xc6 CC_REQ_DATA_INV_LENGTH = 0xc7 CC_REQ_DATA_FIELD_EXCEED = 0xc8 CC_PARAM_OUT_OF_RANGE = 0xc9 CC_CANT_RET_NUM_REQ_BYTES = 0xca CC_REQ_DATA_NOT_PRESENT = 0xcb CC_INV_DATA_FIELD_IN_REQ = 0xcc CC_ILL_SENSOR_OR_RECORD = 0xcd CC_RESP_COULD_NOT_BE_PRV = 0xce CC_CANT_RESP_DUPLI_REQ = 0xcf CC_CANT_RESP_SDRR_UPDATE = 0xd0 CC_CANT_RESP_FIRM_UPDATE = 0xd1 CC_CANT_RESP_BMC_INIT = 0xd2 CC_DESTINATION_UNAVAILABLE = 0xd3 CC_INSUFFICIENT_PRIVILEGES = 0xd4 CC_NOT_SUPPORTED_PRESENT_STATE = 0xd5 CC_ILLEGAL_COMMAND_DISABLED = 0xd6 CC_UNSPECIFIED_ERROR = 0xff COMPLETION_CODE_DESCR = ( (CC_OK, 'Command Completed Normally'), (CC_NODE_BUSY, 'Node Busy'), (CC_INV_CMD, 'Invalid Command'), (CC_INV_CMD_FOR_LUN, 'Command invalid for given LUN'), (CC_TIMEOUT, 'Timeout while processing command'), (CC_OUT_OF_SPACE, 'Out of space'), (CC_RES_CANCELED, 'Reservation Canceled or Invalid Reservation ID'), (CC_REQ_DATA_TRUNC, 'Request data truncated'), (CC_REQ_DATA_INV_LENGTH, 'Request data length invalid'), (CC_REQ_DATA_FIELD_EXCEED, 'Request data field length limit exceeded'), (CC_PARAM_OUT_OF_RANGE, 'Parameter out of range'), (CC_CANT_RET_NUM_REQ_BYTES, 'Cannot return number of requested data bytes'), (CC_REQ_DATA_NOT_PRESENT, 'Requested data not present'), (CC_INV_DATA_FIELD_IN_REQ, 'Invalid data field in Request'), (CC_ILL_SENSOR_OR_RECORD, 'Command illegal for specified sensor or record'), (CC_RESP_COULD_NOT_BE_PRV, 'Command response could not be provided'), (CC_CANT_RESP_DUPLI_REQ, 'Cannot execute duplicated request'), (CC_CANT_RESP_SDRR_UPDATE, 'Command response could not be provided. SDRR in update mode'), (CC_CANT_RESP_FIRM_UPDATE, 'Command response could not be provided. Device in firmware ' 'update mode'), (CC_CANT_RESP_BMC_INIT, 'Command response could not be provided. BMC initialization or ' 'initialization agent in progress'), (CC_DESTINATION_UNAVAILABLE, 'Destination unavailable'), (CC_INSUFFICIENT_PRIVILEGES, 'Cannot execute command due to insufficient privilege level'), (CC_NOT_SUPPORTED_PRESENT_STATE, 'Cannot execute command. Not supported in present state'), (CC_ILLEGAL_COMMAND_DISABLED, 'Cannot execute command. Command sub-function has been disabled ' 'or is unavailable'), (CC_UNSPECIFIED_ERROR, 'Unspecified error'), ) # network functions NETFN_CHASSIS = 0x00 NETFN_BRIDGE = 0x02 NETFN_SENSOR_EVENT = 0x04 NETFN_APP = 0x06 NETFN_FIRMWARE = 0x08 NETFN_STORAGE = 0x0a NETFN_TRANSPORT = 0x0c NETFN_GROUP_EXTENSION = 0x2c NETFN_OEM = 0x2e NETFN_DESCR = ( (NETFN_CHASSIS, 'Chassis'), (NETFN_BRIDGE, 'Bridge'), (NETFN_SENSOR_EVENT, 'Sensor/Event'), (NETFN_APP, 'App'), (NETFN_FIRMWARE, 'Firmware'), (NETFN_STORAGE, 'Storage'), (NETFN_TRANSPORT, 'Transport'), (NETFN_GROUP_EXTENSION, 'Group Extension'), (NETFN_OEM, 'IANA'), ) # IPM device 'global' .. NetFn App CMDID_GET_DEVICE_ID = 0x01 CMDID_COLD_RESET = 0x02 CMDID_WARM_RESET = 0x03 CMDID_GET_SELF_TEST_RESULTS = 0x04 CMDID_MANUFACTURING_TEST_ON = 0x05 CMDID_SET_ACPI_POWER_STATE = 0x06 CMDID_GET_ACPI_POWER_STATE = 0x07 CMDID_GET_DEVICE_GUID = 0x08 CMDID_GET_NETFN_SUPPORT = 0x09 CMDID_GET_COMMAND_SUPPORT = 0x0a CMDID_GET_COMMAND_SUBFUNCTION_SUPPORT = 0x0b CMDID_GET_CONFIGURATION_COMMANDS = 0x0c CMDID_GET_CONFIGURATION_SUBFUNCTION_COMMANDS = 0x0d CMDID_SET_COMMAND_ENABLES = 0x60 CMDID_GET_COMMAND_ENABLES = 0x61 CMDID_SET_COMMAND_SUBFUNCTION_ENABLES = 0x62 CMDID_GET_COMMAND_SUBFUNCTION_ENABLES = 0x63 CMDID_GET_OEM_NETFN_IANA_SUPPORT = 0x64 # BMC watchdog timer .. NetFn App CMDID_RESET_WATCHDOG_TIMER = 0x22 CMDID_SET_WATCHDOG_TIMER = 0x24 CMDID_GET_WATCHDOG_TIMER = 0x25 # BMC device and messaging .. NetFn App CMDID_SET_BMC_GLOBAL_ENABLES = 0x2e CMDID_GET_BMC_GLOBAL_ENABLES = 0x2f CMDID_CLEAR_MESSAGE_FLAGS = 0x30 CMDID_GET_MESSAGE_FLAGS = 0x31 CMDID_ENABLE_MESSAGE_CHANNEL_RECEIVE = 0x32 CMDID_GET_MESSAGE = 0x33 CMDID_SEND_MESSAGE = 0x34 CMDID_READ_EVENT_MESSAGE_BUFFER = 0x35 CMDID_GET_BT_INTERFACE_CAPABILITIES = 0x36 CMDID_GET_SYSTEM_GUID = 0x37 CMDID_GET_CHANNEL_AUTHENTICATION_CAPABILITIES = 0x38 CMDID_GET_SESSION_CHALLENGE = 0x39 CMDID_ACTIVATE_SESSION = 0x3a CMDID_SET_SESSION_PRIVILEGE_LEVEL = 0x3b CMDID_CLOSE_SESSION = 0x3c CMDID_GET_SESSION_INFO = 0x3d CMDID_GET_AUTHCODE = 0x3f CMDID_SET_CHANNEL_ACCESS = 0x40 CMDID_GET_CHANNEL_ACCESS = 0x41 CMDID_GET_CHANNEL_INFO = 0x42 CMDID_SET_USER_ACCESS = 0x43 CMDID_GET_USER_ACCESS = 0x44 CMDID_SET_USER_NAME = 0x45 CMDID_GET_USER_NAME = 0x46 CMDID_SET_USER_PASSWORD = 0x47 CMDID_ACTIVATE_PAYLOAD = 0x48 CMDID_DEACTIVATE_PAYLOAD = 0x49 CMDID_GET_PAYLOAD_ACTITVATION_STATUS = 0x4a CMDID_GET_PAYLOAD_INSTANCE_INFO = 0x4b CMDID_SET_USER_PAYLOAD_ACCESS = 0x4c CMDID_GET_USER_PAYLOAD_ACCESS = 0x4d CMDID_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4e CMDID_GET_CHANNEL_PAYLOAD_VERSION = 0x4f CMDID_GET_CHANNEL_OEM_PAYLOAD_INFO = 0x50 CMDID_MASTER_WRITE_READ = 0x52 CMDID_GET_CHANNEL_CIPHER_SUITES = 0x54 CMDID_SUSPEND_RESUME_PAYLOAD_ENCRYPTION = 0x55 CMDID_SET_CHANNEL_SECURITY_KEYS = 0x56 CMDID_GET_SYSTEM_INTERFACE_CAPABILITIES = 0x57 CMDID_SET_SYSTEM_INFO_PARAMETERS = 0x58 CMDID_GET_SYSTEM_INFO_PARAMETERS = 0x59 # chassis device .. NetFn Chassis CMDID_GET_CHASSIS_CAPABILITIES = 0x00 CMDID_GET_CHASSIS_STATUS = 0x01 CMDID_CHASSIS_CONTROL = 0x02 CMDID_CHASSIS_RESET = 0x03 CMDID_CHASSIS_IDENTIFY = 0x04 CMDID_SET_CHASSIS_CAPABILITIES = 0x05 CMDID_SET_POWER_RESTORE_POLICY = 0x06 CMDID_GET_SYSTEM_RESTART_CAUSE = 0x07 CMDID_SET_SYSTEM_BOOT_OPTIONS = 0x08 CMDID_GET_SYSTEM_BOOT_OPTIONS = 0x09 CMDID_GET_POH_COUNTER = 0x0f # event .. NetFn S/E CMDID_SET_EVENT_RECEIVER = 0x00 CMDID_GET_EVENT_RECEIVER = 0x01 CMDID_PLATFORM_EVENT = 0x02 # PEF and alerting .. NetFn S/E CMDID_GET_PEF_CAPABILITIES = 0x10 CMDID_ARM_PEF_POSTPONE_TIMER = 0x11 CMDID_SET_PEFCONFIGURATION_PARAMETERS = 0x12 CMDID_GET_PEFCONFIGURATION_PARAMETERS = 0x13 CMDID_SET_LAST_PROCESSED_EVENT_ID = 0x14 CMDID_GET_LAST_PROCESSED_EVENT_ID = 0x15 CMDID_ALERT_IMMEDIATE = 0x16 CMDID_PET_ACKNOWLEDGE = 0x17 # sensor device .. NetFn S/E CMDID_GET_DEVICE_SDR_INFO = 0x20 CMDID_GET_DEVICE_SDR = 0x21 CMDID_RESERVE_DEVICE_SDR_REPOSITORY = 0x22 CMDID_GET_SENSOR_READING_FACTOR = 0x23 CMDID_SET_SENSOR_HYSTERESIS = 0x24 CMDID_GET_SENSOR_HYSTERESIS = 0x25 CMDID_SET_SENSOR_THRESHOLD = 0x26 CMDID_GET_SENSOR_THRESHOLD = 0x27 CMDID_SET_SENSOR_EVENT_ENABLE = 0x28 CMDID_GET_SENSOR_EVENT_ENABLE = 0x29 CMDID_RE_ARM_SENSOR = 0x2a CMDID_GET_SENSOR_EVENT_STATUS = 0x2b CMDID_GET_SENSOR_READING = 0x2d CMDID_SET_SENSOR_TYPE = 0x2e CMDID_GET_SENSOR_TYPE = 0x2f CMDID_SET_SENSOR_READING_AND_EVENT_STATUS = 0x30 # fru device .. NetFn Storage CMDID_GET_FRU_INVENTORY_AREA_INFO = 0x10 CMDID_READ_FRU_DATA = 0x11 CMDID_WRITE_FRU_DATA = 0x12 # SDR device .. NetFn Storage CMDID_GET_SDR_REPOSITORY_INFO = 0x20 CMDID_GET_SDR_REPOSITORY_ALLOCATION_INFO = 0x21 CMDID_RESERVE_SDR_REPOSITORY = 0x22 CMDID_GET_SDR = 0x23 CMDID_ADD_SDR = 0x24 CMDID_PARTIAL_ADD_SDR = 0x25 CMDID_DELETE_SDR = 0x26 CMDID_CLEAR_SDR_REPOSITORY = 0x27 CMDID_GET_SDR_REPOSITORY_TIME = 0x28 CMDID_SET_SDR_REPOSITORY_TIME = 0x29 CMDID_ENTER_SDR_REPOSITORY_UPDATE_MODE = 0x2a CMDID_EXIT_SDR_REPOSITORY_UPDATE_MODE = 0x2b CMDID_RUN_INITIALIZATION_AGENT = 0x2c # SEL device .. NetFn Storage CMDID_GET_SEL_INFO = 0x40 CMDID_GET_SEL_ALLOCATION_INFO = 0x41 CMDID_RESERVE_SEL = 0x42 CMDID_GET_SEL_ENTRY = 0x43 CMDID_ADD_SEL_ENTRY = 0x44 CMDID_PARTIAL_ADD_SEL_ENTRY = 0x45 CMDID_DELETE_SEL_ENTRY = 0x46 CMDID_CLEAR_SEL = 0x47 CMDID_GET_SEL_TIME = 0x48 CMDID_SET_SEL_TIME = 0x49 CMDID_GET_AUXILIARY_LOG_STATUS = 0x5a CMDID_SET_AUXILIARY_LOG_STATUS = 0x5b CMDID_GET_SEL_TIME_UTC_OFFSET = 0x5c CMDID_SET_SEL_TIME_UTC_OFFSET = 0x5d # LAN device .. NetFn Transport CMDID_SET_LAN_CONFIGURATION_PARAMETERS = 0x01 CMDID_GET_LAN_CONFIGURATION_PARAMETERS = 0x02 CMDID_SUSPEND_BMC_ARPS = 0x03 CMDID_GET_IP_UDP_RMCP_STATISTICS = 0x04 # Serial/Modem device .. NetFn Transport # Command Forwarding .. NetFn Transport # bridge management (ICMB) .. NetFn Bridge # discovery (ICMB) .. NetFn Bridge # bridging (ICMB) .. NetFn Bridge # event (ICMB) .. NetFn Bridge # other (ICMB) .. NetFn Bridge # PICMG commands .. NetFn Group Extension (ID 0) CMDID_GET_PICMG_PROPERTIES = 0x00 CMDID_GET_ADDRESS_INFO = 0x01 CMDID_GET_SHELF_ADDRESS_INFO = 0x02 CMDID_SET_SHELF_ADDRESS_INFO = 0x03 CMDID_FRU_CONTROL = 0x04 CMDID_GET_FRU_LED_PROPERTIES = 0x05 CMDID_GET_FRU_LED_COLOR_CAPABILITIES = 0x06 CMDID_SET_FRU_LED_STATE = 0x07 CMDID_GET_FRU_LED_STATE = 0x08 CMDID_SET_IPMB_STATE = 0x09 CMDID_SET_FRU_ACTIVATION_POLICY = 0x0a CMDID_GET_FRU_ACTIVATION_POLICY = 0x0b CMDID_SET_FRU_ACTIVATION = 0x0c CMDID_GET_DEVLOC_RECORD_ID = 0x0d CMDID_SET_PORT_STATE = 0x0e CMDID_GET_PORT_STATE = 0x0f CMDID_COMPUTE_POWER_PROPERTIES = 0x10 CMDID_SET_POWER_LEVEL = 0x11 CMDID_GET_POWER_LEVEL = 0x12 CMDID_RENEGOTIATE_POWER = 0x13 CMDID_GET_FAN_SPEED_PROPERTIES = 0x14 CMDID_SET_FAN_LEVEL = 0x15 CMDID_GET_FAN_LEVEL = 0x16 CMDID_BUSED_RESOURCE = 0x17 CMDID_GET_IPMB_LINK_INFO = 0x18 CMDID_GET_SHELF_MANAGER_IPMB_ADDRESS = 0x1b CMDID_SET_FAN_POLICY = 0x1c CMDID_GET_FAN_POLICY = 0x1d CMDID_FRU_CONTROL_CAPABILITIES = 0x1e CMDID_FRU_INVENTORY_DEVICE_LOCK_CONTROL = 0x1f CMDID_FRU_INVENTORY_DEVICE_WRITE = 0x20 CMDID_GET_SHELF_MANAGER_IP_ADDRESSES = 0x21 CMDID_GET_SHELF_POWER_ALLOCATION = 0x22 CMDID_SET_CHANNEL_SIGNALING_CLASS = 0x3b CMDID_GET_CHANNEL_SIGNALING_CLASS = 0x3c # PICMG AdvancedMC .. NetFn Group Extension (ID 0) CMDID_SET_AMC_PORT_STATE = 0x19 CMDID_GET_AMC_PORT_STATE = 0x1a # PICMG MicroTCA .. NetFn Group Extension (ID 0) CMDID_GET_LOCATION_INFO = 0x23 CMDID_POWER_CHANNEL_CONTROL = 0x24 CMDID_GET_POWER_CHANNEL_STATUS = 0x25 CMDID_PM_RESET = 0x26 CMDID_GET_PM_STATUS = 0x27 CMDID_PM_HEARTBEAT = 0x28 CMDID_GET_TELCO_ALARM_CAPABILITY = 0x29 CMDID_SET_TELCO_ALARM_STATE = 0x2a CMDID_GET_TELCO_ALARM_STATE = 0x2b # PICMG HPM.1 commands .. NetFn Group Extension (ID 0) CMDID_HPM_GET_TARGET_UPGRADE_CAPABILITIES = 0x2e CMDID_HPM_GET_COMPONENT_PROPERTIES = 0x2f CMDID_HPM_ABORT_FIRMWARE_UPGRADE = 0x30 CMDID_HPM_INITIATE_UPGRADE_ACTION = 0x31 CMDID_HPM_UPLOAD_FIRMWARE_BLOCK = 0x32 CMDID_HPM_FINISH_FIRMWARE_UPLOAD = 0x33 CMDID_HPM_GET_UPGRADE_STATUS = 0x34 CMDID_HPM_ACTIVATE_FIRMWARE = 0x35 CMDID_HPM_QUERY_SELFTEST_RESULTS = 0x36 CMDID_HPM_QUERY_ROLLBACK_STATUS = 0x37 CMDID_HPM_INITIATE_MANUAL_ROLLBACK = 0x38 # PICMG HPM.2 commands .. NetFn Group Extension (ID 0) CMDID_GET_LAN_ATTACH_CAPABILITIES = 0x3e # VITA commands .. NetFn Group Extension (ID 0x03) CMDID_VITA_GET_VSO_CAPABILITIES = 0x00 CMDID_VITA_GET_CHASSIS_ADDRESS_TABLE_INFO = 0x01 CMDID_VITA_GET_CHASSIS_IDENTIFIER = 0x02 CMDID_VITA_SET_CHASSIS_IDENTIFIER = 0x03 CMDID_VITA_FRU_CONTROL = 0x04 CMDID_VITA_GET_FRU_LED_PROPERTIES = 0x05 CMDID_VITA_GET_LED_COLOR_CAPABILITIES = 0x06 CMDID_VITA_SET_FRU_LED_STATE = 0x07 CMDID_VITA_GET_FRU_LED_STATE = 0x08 CMDID_VITA_SET_IPMB_STATE = 0x09 CMDID_VITA_SET_FRU_STATE_POLICY_BITS = 0x0a CMDID_VITA_GET_FRU_STATE_POLICY_BITS = 0x0b CMDID_VITA_SET_FRU_ACTIVATION = 0x0c CMDID_VITA_GET_DEVICE_LOCATOR_RECORD_ID = 0x0d CMDID_VITA_GET_FAN_SPEED_PROPERTIES = 0x14 CMDID_VITA_SET_FAN_LEVEL = 0x15 CMDID_VITA_GET_FAN_LEVEL = 0x16 CMDID_VITA_GET_IPMB_LINK_INFO = 0x18 CMDID_VITA_GET_CHASSIS_MANAGER_IPMB_ADDRESS = 0x1b CMDID_VITA_SET_FAN_POLICY = 0x1c CMDID_VITA_GET_FAN_POLICY = 0x1d CMDID_VITA_FRU_CONTROL_CAPABILITIES = 0x1e CMDID_VITA_FRU_INVENTROY_DEVICE_LOCK_CONTROL = 0x1f CMDID_VITA_FRU_INVENTROY_DEVICE_WRITE = 0x20 CMDID_VITA_GET_CHASSIS_MANAGER_IP_ADDRESSES = 0x21 CMDID_VITA_GET_FRU_ADDRESS_INFO = 0x40 CMDID_VITA_GET_FRU_PERSISTENT_CONTROL = 0x41 CMDID_VITA_SET_FRU_PERSISTENT_CONTROL = 0x42 CMDID_VITA_FRU_PERSISTENT_CONTROL_CAPABILITIES = 0x43 CMDID_VITA_GET_MANDATORY_SENSOR_NUMBERS = 0x44 # DCMI commands .. NetFn Group Extension (ID 0xdc) CMDID_GET_DCMI_CAPABILITIES_INFO = 0x01 CMDID_GET_POWER_READING = 0x02 CMDID_GET_POWER_LIMIT = 0x03 CMDID_SET_POWER_LIMIT = 0x04 CMDID_ACTIVATE_DEACTIVATE_POWER_LIMIT = 0x05 CMDID_GET_ASSET_TAG = 0x06 CMDID_GET_DCMI_SENSOR_INFO = 0x07 CMDID_SET_ASSET_TAG = 0x08 CMDID_GET_MANAGEMENT_CONTROLLER_ID_STRING = 0x09 CMDID_SET_MANAGEMENT_CONTROLLER_ID_STRING = 0x0a cc_err_cmd_specific_desc = { CMDID_GET_SESSION_CHALLENGE: { 0x81: 'invalid user name', 0x82: 'null user name (User 1) not enabled', }, CMDID_ACTIVATE_SESSION: { 0x81: 'No session slot available (BMC cannot accept any more' ' sessions)', 0x82: 'No slot available for given user', 0x83: 'No slot available to support user due to maximum privilege' ' capability', 0x84: 'session sequence number out-of-range', 0x85: 'invalid Session ID in request', 0x86: 'requested maximum privilege level exceeds user and/or channel' ' privilege limit', }, } REPOSITORY_INITIATE_ERASE = 0xaa REPOSITORY_GET_ERASE_STATUS = 0x00 REPOSITORY_ERASURE_IN_PROGRESS = 0x0 REPOSITORY_ERASURE_COMPLETED = 0x1 python-ipmi-0.5.4/pyipmi/msgs/dcmi.py000066400000000000000000000201351436677633700175720ustar00rootroot00000000000000# Copyright (c) 2018 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from . import constants from . import register_message_class from . import Bitfield from . import CompletionCode from . import GroupExtensionIdentifier from . import Message from . import RemainingBytes from . import Timestamp from . import UnsignedInt DCMI_GROUP_CODE = 0xdc class DcmiMessage(Message): __group_extension__ = DCMI_GROUP_CODE @register_message_class class GetDcmiCapabilitiesReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_DCMI_CAPABILITIES_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), UnsignedInt('parameter_selector', 1), ) @register_message_class class GetDcmiCapabilitiesRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_DCMI_CAPABILITIES_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), Bitfield('specification_conformence', 2, Bitfield.Bit('major', 8), Bitfield.Bit('minor', 8)), UnsignedInt('parameter_revision', 1), RemainingBytes('parameter_data'), ) @register_message_class class GetPowerReadingReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_POWER_READING __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), UnsignedInt('mode', 1), UnsignedInt('attributes', 1), UnsignedInt('reserved', 1), ) @register_message_class class GetPowerReadingRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_POWER_READING __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), UnsignedInt('current_power', 2), UnsignedInt('minimum_power', 2), UnsignedInt('maximum_power', 2), UnsignedInt('average_power', 2), Timestamp('timestamp'), UnsignedInt('period', 4), UnsignedInt('reading_state', 1), ) @register_message_class class GetPowerLimitReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetPowerLimitRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class SetPowerLimitReq(DcmiMessage): __cmdid__ = constants.CMDID_SET_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class SetPowerLimitRsp(DcmiMessage): __cmdid__ = constants.CMDID_SET_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetActivateDeactivatePowerLimitReq(DcmiMessage): __cmdid__ = constants.CMDID_ACTIVATE_DEACTIVATE_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetActivateDeactivatePowerLimitRsp(DcmiMessage): __cmdid__ = constants.CMDID_ACTIVATE_DEACTIVATE_POWER_LIMIT __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetAssetTagReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_ASSET_TAG __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetAssetTagRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_ASSET_TAG __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetDcmiSensorInfoReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_DCMI_SENSOR_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), UnsignedInt('sensor_type', 1), UnsignedInt('entity_id', 1), UnsignedInt('entity_instance', 1), UnsignedInt('entity_instance_start', 1), ) @register_message_class class GetDcmiSensorInfoRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_DCMI_SENSOR_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), UnsignedInt('total_number_of_instances', 1), UnsignedInt('number_of_record_ids', 1), RemainingBytes('record_ids'), ) @register_message_class class SetAssetTagReq(DcmiMessage): __cmdid__ = constants.CMDID_SET_ASSET_TAG __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class SetAssetTagRsp(DcmiMessage): __cmdid__ = constants.CMDID_SET_ASSET_TAG __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetManagementControllerIdStringReq(DcmiMessage): __cmdid__ = constants.CMDID_GET_MANAGEMENT_CONTROLLER_ID_STRING __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class GetManagementControllerIdStringRsp(DcmiMessage): __cmdid__ = constants.CMDID_GET_MANAGEMENT_CONTROLLER_ID_STRING __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class SetManagementControllerIdStringReq(DcmiMessage): __cmdid__ = constants.CMDID_SET_MANAGEMENT_CONTROLLER_ID_STRING __netfn__ = constants.NETFN_GROUP_EXTENSION __not_implemented__ = True __fields__ = ( GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) @register_message_class class SetManagementControllerIdStringRsp(DcmiMessage): __cmdid__ = constants.CMDID_SET_MANAGEMENT_CONTROLLER_ID_STRING __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __not_implemented__ = True __fields__ = ( CompletionCode(), GroupExtensionIdentifier('group_extension_id', DCMI_GROUP_CODE), ) python-ipmi-0.5.4/pyipmi/msgs/device_messaging.py000066400000000000000000000401341436677633700221530ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import UnsignedInt from . import Bitfield from . import Optional from . import String from . import CompletionCode from . import RemainingBytes @register_message_class class SetBmcGlobalEnablesReq(Message): __cmdid__ = constants.CMDID_SET_BMC_GLOBAL_ENABLES __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('enables', 1, Bitfield.Bit('receive_message_queue_interrupt', 1, 0), Bitfield.Bit('event_message_buffer_full_interrupt', 1, 0), Bitfield.Bit('event_message_buffer', 1, 0), Bitfield.Bit('system_event_logging', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('oem_0', 1, 0), Bitfield.Bit('oem_1', 1, 0), Bitfield.Bit('oem_2', 1, 0),), ) @register_message_class class SetBmcGlobalEnablesRsp(Message): __cmdid__ = constants.CMDID_SET_BMC_GLOBAL_ENABLES __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetBmcGlobalEnablesReq(Message): __cmdid__ = constants.CMDID_GET_BMC_GLOBAL_ENABLES __netfn__ = constants.NETFN_APP @register_message_class class GetBmcGlobalEnablesRsp(Message): __cmdid__ = constants.CMDID_GET_BMC_GLOBAL_ENABLES __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('enables', 1, Bitfield.Bit('receive_message_queue_interrupt', 1, 0), Bitfield.Bit('event_message_buffer_full_interrupt', 1, 0), Bitfield.Bit('event_message_buffer', 1, 0), Bitfield.Bit('system_event_logging', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('oem_0', 1, 0), Bitfield.Bit('oem_1', 1, 0), Bitfield.Bit('oem_2', 1, 0),), ) @register_message_class class ClearMessageFlagsReq(Message): __cmdid__ = constants.CMDID_CLEAR_MESSAGE_FLAGS __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('clear', 1, Bitfield.Bit('receive_message_queue', 1, 0), Bitfield.Bit('event_message_buffer', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('watchdog_pretimeout_interrupt_flag', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('oem_0', 1, 0), Bitfield.Bit('oem_1', 1, 0), Bitfield.Bit('oem_2', 1, 0),), ) @register_message_class class ClearMessageFlagsRsp(Message): __cmdid__ = constants.CMDID_CLEAR_MESSAGE_FLAGS __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetMessageFlagsReq(Message): __cmdid__ = constants.CMDID_GET_MESSAGE_FLAGS __netfn__ = constants.NETFN_APP @register_message_class class GetMessageFlagsRsp(Message): __cmdid__ = constants.CMDID_GET_MESSAGE_FLAGS __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('flag', 1, Bitfield.Bit('receive_message_available', 1, 0), Bitfield.Bit('event_message_buffer_full', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('watchdog_pretimeout_interrupt_occurred', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('oem_0', 1, 0), Bitfield.Bit('oem_1', 1, 0), Bitfield.Bit('oem_2', 1, 0),), ) @register_message_class class EnableMessageChannelReceiveReq(Message): __cmdid__ = constants.CMDID_ENABLE_MESSAGE_CHANNEL_RECEIVE __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('channel', 2, Bitfield.Bit('number', 4, 0), Bitfield.ReservedBit(4, 0), Bitfield.Bit('state', 2, 0), Bitfield.ReservedBit(6, 0),), ) @register_message_class class EnableMessageChannelReceiveRsp(Message): __cmdid__ = constants.CMDID_ENABLE_MESSAGE_CHANNEL_RECEIVE __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('channel', 2, Bitfield.Bit('number', 4, 0), Bitfield.ReservedBit(4, 0), Bitfield.Bit('state', 1, 0), Bitfield.ReservedBit(7, 0),), ) @register_message_class class GetMessageReq(Message): __cmdid__ = constants.CMDID_GET_MESSAGE __netfn__ = constants.NETFN_APP @register_message_class class GetMessageRsp(Message): __cmdid__ = constants.CMDID_GET_MESSAGE __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('channel', 1, Bitfield.Bit('number', 4, 0), Bitfield.Bit('privilege_level', 4, 0),), RemainingBytes('data'), ) @register_message_class class SendMessageReq(Message): __cmdid__ = constants.CMDID_SEND_MESSAGE __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('channel', 1, Bitfield.Bit('number', 4, 0), Bitfield.Bit('authenticated', 1, 0), Bitfield.Bit('encrypted', 1, 0), Bitfield.Bit('tracking', 2, 0),), RemainingBytes('data'), ) @register_message_class class SendMessageRsp(Message): __cmdid__ = constants.CMDID_SEND_MESSAGE __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), RemainingBytes('data'), ) @register_message_class class ReadEventMessageBufferReq(Message): __cmdid__ = constants.CMDID_READ_EVENT_MESSAGE_BUFFER __netfn__ = constants.NETFN_APP @register_message_class class ReadEventMessageBufferRsp(Message): __cmdid__ = constants.CMDID_READ_EVENT_MESSAGE_BUFFER __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), RemainingBytes('event_data'), ) @register_message_class class MasterWriteReadReq(Message): __cmdid__ = constants.CMDID_MASTER_WRITE_READ __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('bus_id', 2, Bitfield.Bit('type', 1, 0), Bitfield.Bit('id', 3, 0), Bitfield.Bit('channel', 4, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('slave_address', 7, 0),), UnsignedInt('read_count', 1), RemainingBytes('data'), ) @register_message_class class MasterWriteReadRsp(Message): __cmdid__ = constants.CMDID_MASTER_WRITE_READ __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), RemainingBytes('data'), ) @register_message_class class GetChannelAuthenticationCapabilitiesReq(Message): __cmdid__ = constants.CMDID_GET_CHANNEL_AUTHENTICATION_CAPABILITIES __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('channel', 1, Bitfield.Bit('number', 4, 0), Bitfield.ReservedBit(3, 0), Bitfield.Bit('type', 1, 0),), Bitfield('privilege_level', 1, Bitfield.Bit('requested', 4, 0), Bitfield.ReservedBit(4, 0),), ) @register_message_class class GetChannelAuthenticationCapabilitiesRsp(Message): __cmdid__ = constants.CMDID_GET_CHANNEL_AUTHENTICATION_CAPABILITIES __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), UnsignedInt('channel_number', 1), Bitfield('support', 1, Bitfield.Bit('none', 1, 0), Bitfield.Bit('md2', 1, 0), Bitfield.Bit('md5', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('straight', 1, 0), Bitfield.Bit('oem_proprietary', 1, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('ipmi_2_0', 1, 0),), Bitfield('status', 1, Bitfield.Bit('anonymous_login_enabled', 1, 0), Bitfield.Bit('anonymous_login_null_user', 1, 0), Bitfield.Bit('anonymous_login_non_null', 1, 0), Bitfield.Bit('user_level', 1, 0), Bitfield.Bit('per_message', 1, 0), Bitfield.Bit('kg', 1, 0), Bitfield.ReservedBit(2, 0),), Bitfield('extended_capabilities', 1, Bitfield.Bit('1_5', 1, 0), Bitfield.Bit('2_0', 1, 0), Bitfield.ReservedBit(6, 0),), UnsignedInt('oem_id', 3), UnsignedInt('oem_auxiliary_data', 1), ) @register_message_class class GetSessionChallengeReq(Message): __cmdid__ = constants.CMDID_GET_SESSION_CHALLENGE __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('authentication', 1, Bitfield.Bit('type', 4, 0), Bitfield.ReservedBit(4, 0),), String('user_name', 16, '\x00' * 16), ) @register_message_class class GetSessionChallengeRsp(Message): __cmdid__ = constants.CMDID_GET_SESSION_CHALLENGE __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), UnsignedInt('temporary_session_id', 4), String('challenge_string', 16, '\x00' * 16), ) @register_message_class class ActivateSessionReq(Message): __cmdid__ = constants.CMDID_ACTIVATE_SESSION __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('authentication', 1, Bitfield.Bit('type', 4, 0), Bitfield.ReservedBit(4, 0),), Bitfield('privilege_level', 1, Bitfield.Bit('maximum_requested', 4, 0), Bitfield.ReservedBit(4, 0),), String('challenge_string', 16), UnsignedInt('initial_outbound_sequence_number', 4), ) @register_message_class class ActivateSessionRsp(Message): __cmdid__ = constants.CMDID_ACTIVATE_SESSION __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('authentication', 1, Bitfield.Bit('type', 4, 0), Bitfield.ReservedBit(4, 0),), UnsignedInt('session_id', 4), UnsignedInt('initial_inbound_sequence_number', 4), Bitfield('privilege_level', 1, Bitfield.Bit('maximum_allowed', 4, 0), Bitfield.ReservedBit(4, 0),), ) @register_message_class class SetSessionPrivilegeLevelReq(Message): __cmdid__ = constants.CMDID_SET_SESSION_PRIVILEGE_LEVEL __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('privilege_level', 1, Bitfield.Bit('requested', 4, 0), Bitfield.ReservedBit(4, 0),), ) @register_message_class class SetSessionPrivilegeLevelRsp(Message): __cmdid__ = constants.CMDID_SET_SESSION_PRIVILEGE_LEVEL __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('privilege_level', 1, Bitfield.Bit('new', 4, 0), Bitfield.ReservedBit(4, 0),), ) @register_message_class class CloseSessionReq(Message): __cmdid__ = constants.CMDID_CLOSE_SESSION __netfn__ = constants.NETFN_APP __fields__ = ( UnsignedInt('session_id', 4), ) @register_message_class class CloseSessionRsp(Message): __cmdid__ = constants.CMDID_CLOSE_SESSION __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class SetUserNameReq(Message): __cmdid__ = constants.CMDID_SET_USER_NAME __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('userid', 1, Bitfield.Bit('userid', 6, 0), Bitfield.ReservedBit(2, 0),), String('user_name', 16), ) @register_message_class class SetUserNameRsp(Message): __cmdid__ = constants.CMDID_SET_USER_NAME __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetUserNameReq(Message): __cmdid__ = constants.CMDID_GET_USER_NAME __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('userid', 1, Bitfield.Bit('userid', 6, 0), Bitfield.ReservedBit(2, 0),), ) @register_message_class class GetUserNameRsp(Message): __cmdid__ = constants.CMDID_GET_USER_NAME __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), String('user_name', 16, '\x00' * 16), ) @register_message_class class SetUserPasswordReq(Message): __cmdid__ = constants.CMDID_SET_USER_PASSWORD __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('userid', 1, Bitfield.Bit('userid', 6, 0), Bitfield.ReservedBit(1, 0), Bitfield.Bit('password_size', 1, 0)), Bitfield('operation', 1, Bitfield.Bit('operation', 2, 0), Bitfield.ReservedBit(6, 0)), String('password', 16, '\x00' * 16) ) @register_message_class class SetUserPasswordRsp(Message): __cmdid__ = constants.CMDID_SET_USER_PASSWORD __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetUserAccessReq(Message): __cmdid__ = constants.CMDID_GET_USER_ACCESS __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('channel', 1, Bitfield.Bit('channel_number', 4, 0), Bitfield.ReservedBit(4, 0)), Bitfield('userid', 1, Bitfield.Bit('userid', 6, 0), Bitfield.ReservedBit(2, 0)) ) @register_message_class class GetUserAccessRsp(Message): __cmdid__ = constants.CMDID_GET_USER_ACCESS __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), Bitfield('max_user', 1, Bitfield.Bit('max_user', 6, 0), Bitfield.ReservedBit(2, 0)), Bitfield('enabled_user', 1, Bitfield.Bit('count', 6, 0), Bitfield.Bit('status', 2, 0)), Bitfield('fixed_names', 1, Bitfield.Bit('count', 6, 0), Bitfield.ReservedBit(2, 0)), Bitfield('channel_access', 1, Bitfield.Bit('privilege', 4, 0), Bitfield.Bit('ipmi_msg', 1, 0), Bitfield.Bit('link_auth', 1, 0), Bitfield.Bit('callback', 1, 0), Bitfield.ReservedBit(1, 0)) ) @register_message_class class SetUserAccessReq(Message): __cmdid__ = constants.CMDID_SET_USER_ACCESS __netfn__ = constants.NETFN_APP __fields__ = ( Bitfield('channel_access', 1, Bitfield.Bit('channel_number', 4, 0), Bitfield.Bit('ipmi_msg', 1, 0), Bitfield.Bit('link_auth', 1, 0), Bitfield.Bit('callback', 1, 0), Bitfield.Bit('enable_change', 1, 0)), Bitfield('userid', 1, Bitfield.Bit('userid', 6, 0), Bitfield.ReservedBit(2, 0)), Bitfield('privilege', 1, Bitfield.Bit('privilege_level', 4, 0x0f), Bitfield.ReservedBit(4, 0)), Optional( Bitfield('session_limit', 1, Bitfield.Bit('simultaneous_session_limit', 4, 1), Bitfield.ReservedBit(4, 0)) ) ) @register_message_class class SetUserAccessRsp(Message): __cmdid__ = constants.CMDID_SET_USER_ACCESS __netfn__ = constants.NETFN_APP | 1 __fields__ = ( CompletionCode(), ) python-ipmi-0.5.4/pyipmi/msgs/event.py000066400000000000000000000042131436677633700177760ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import Bitfield from . import CompletionCode @register_message_class class SetEventReceiverReq(Message): __cmdid__ = constants.CMDID_SET_EVENT_RECEIVER __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( Bitfield('event_receiver', 2, Bitfield.ReservedBit(1, 0), Bitfield.Bit('ipmb_i2c_slave_address', 7, 0), Bitfield.Bit('lun', 2, 0), Bitfield.ReservedBit(6, 0),), ) @register_message_class class SetEventReceiverRsp(Message): __cmdid__ = constants.CMDID_SET_EVENT_RECEIVER __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetEventReceiverReq(Message): __cmdid__ = constants.CMDID_GET_EVENT_RECEIVER __netfn__ = constants.NETFN_SENSOR_EVENT @register_message_class class GetEventReceiverRsp(Message): __cmdid__ = constants.CMDID_GET_EVENT_RECEIVER __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), Bitfield('event_receiver', 2, Bitfield.ReservedBit(1, 0), Bitfield.Bit('ipmb_i2c_slave_address', 7, 0), Bitfield.Bit('lun', 2, 0), Bitfield.ReservedBit(6, 0),), ) python-ipmi-0.5.4/pyipmi/msgs/fru.py000066400000000000000000000053141436677633700174540ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import UnsignedInt from . import Bitfield from . import CompletionCode from . import RemainingBytes from . import VariableByteArray @register_message_class class GetFruInventoryAreaInfoReq(Message): __cmdid__ = constants.CMDID_GET_FRU_INVENTORY_AREA_INFO __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('fru_id', 1, 0), ) @register_message_class class GetFruInventoryAreaInfoRsp(Message): __cmdid__ = constants.CMDID_GET_FRU_INVENTORY_AREA_INFO __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('area_size', 2), Bitfield('area_info', 1, Bitfield.Bit('access', 1), Bitfield.ReservedBit(7, 0)), ) @register_message_class class ReadFruDataReq(Message): __cmdid__ = constants.CMDID_READ_FRU_DATA __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('fru_id', 1), UnsignedInt('offset', 2), UnsignedInt('count', 1), ) @register_message_class class ReadFruDataRsp(Message): __cmdid__ = constants.CMDID_READ_FRU_DATA __netfn__ = constants.NETFN_STORAGE | 1 def _length_count(obj): return obj.count __fields__ = ( CompletionCode(), UnsignedInt('count', 1), VariableByteArray('data', _length_count), ) @register_message_class class WriteFruDataReq(Message): __cmdid__ = constants.CMDID_WRITE_FRU_DATA __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('fru_id', 1), UnsignedInt('offset', 2), RemainingBytes('data'), ) @register_message_class class WriteFruDataRsp(Message): __cmdid__ = constants.CMDID_WRITE_FRU_DATA __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('count_written', 1) ) python-ipmi-0.5.4/pyipmi/msgs/hpm.py000066400000000000000000000243471436677633700174530ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import UnsignedInt from . import Bitfield from . import CompletionCode from . import Optional from . import RemainingBytes from . import GroupExtensionIdentifier from .picmg import PicmgMessage, PICMG_IDENTIFIER @register_message_class class GetTargetUpgradeCapabilitiesReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_TARGET_UPGRADE_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetTargetUpgradeCapabilitiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_TARGET_UPGRADE_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('hpm_1_version', 1), Bitfield('capabilities', 1, Bitfield.Bit('firmware_upgrade_undesirable', 1), Bitfield.Bit('automatic_rollback_overriden', 1), Bitfield.Bit('ipmc_degraded_during_upgrade', 1), Bitfield.Bit('deferred_activation', 1), Bitfield.Bit('services_affected_by_upgrade', 1), Bitfield.Bit('manual_rollback', 1), Bitfield.Bit('automatic_rollback', 1), Bitfield.Bit('selftest', 1),), Bitfield('timeout', 4, Bitfield.Bit('upgrade', 8), Bitfield.Bit('selftest', 8), Bitfield.Bit('rollback', 8), Bitfield.Bit('inaccessibility', 8),), UnsignedInt('component_present', 1), ) @register_message_class class GetComponentPropertiesReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_COMPONENT_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('id', 1), UnsignedInt('selector', 1), ) @register_message_class class GetComponentPropertiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_COMPONENT_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), RemainingBytes('data'), ) @register_message_class class AbortFirmwareUpgradeReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_ABORT_FIRMWARE_UPGRADE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class AbortFirmwareUpgradeRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_ABORT_FIRMWARE_UPGRADE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class InitiateUpgradeActionReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_INITIATE_UPGRADE_ACTION __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('components', 1), UnsignedInt('action', 1), ) @register_message_class class InitiateUpgradeActionRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_INITIATE_UPGRADE_ACTION __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class UploadFirmwareBlockReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_UPLOAD_FIRMWARE_BLOCK __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('number', 1), RemainingBytes('data'), ) @register_message_class class UploadFirmwareBlockRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_UPLOAD_FIRMWARE_BLOCK __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Optional(UnsignedInt('section_offset', 4)), Optional(UnsignedInt('section_length', 4)), ) @register_message_class class FinishFirmwareUploadReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_FINISH_FIRMWARE_UPLOAD __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('component_id', 1), UnsignedInt('image_length', 4), ) @register_message_class class FinishFirmwareUploadRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_FINISH_FIRMWARE_UPLOAD __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetUpgradeStatusReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_UPGRADE_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetUpgradeStatusRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_GET_UPGRADE_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('command_in_progress', 1), UnsignedInt('last_completion_code', 1), Optional(UnsignedInt('completion_estimate', 1)), ) @register_message_class class ActivateFirmwareReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_ACTIVATE_FIRMWARE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Optional(UnsignedInt('rollback_override_policy', 1)), ) @register_message_class class ActivateFirmwareRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_ACTIVATE_FIRMWARE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class QuerySelftestResultsReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_QUERY_SELFTEST_RESULTS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class QuerySelftestResultsRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_QUERY_SELFTEST_RESULTS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('selftest_result_1', 1), UnsignedInt('selftest_result_2', 1), ) @register_message_class class QueryRollbackStatusReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_QUERY_ROLLBACK_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class QueryRollbackStatusRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_QUERY_ROLLBACK_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('rollback_status', 1), Optional(UnsignedInt('completion_estimate', 1)), ) @register_message_class class InitiateManualRollbackReq(PicmgMessage): __cmdid__ = constants.CMDID_HPM_INITIATE_MANUAL_ROLLBACK __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class InitiateManualRollbackRsp(PicmgMessage): __cmdid__ = constants.CMDID_HPM_INITIATE_MANUAL_ROLLBACK __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetLanAttachCapabilitiesReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_LAN_ATTACH_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Optional(UnsignedInt('optional', 1)), ) @register_message_class class GetLanAttachCapabilitiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_LAN_ATTACH_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('hpm2_revision_identifier', 1), UnsignedInt('lan_channel_mask', 2), Optional(UnsignedInt('hpm2_capabilities', 1)), Optional(UnsignedInt('hpm3_revision_identifier', 1)), Optional(UnsignedInt('hpm2_oem_lan_params_start_location', 1)), Optional(UnsignedInt('hpm2_oem_lan_params_blocks_revision_number', 1)), Optional(UnsignedInt('hpm2_oem_sol_payload_instances_params_start_location', 1)), Optional(UnsignedInt('hpm2_oem_sol_payload_instances_params_blocks_revision_number', 1)), Optional(UnsignedInt('hpm3_oem_lan_params_start_location', 1)), Optional(UnsignedInt('hpm3_oem_lan_params_blocks_revision_number', 1)), ) python-ipmi-0.5.4/pyipmi/msgs/lan.py000066400000000000000000000050571436677633700174360ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import ByteArray from . import UnsignedInt from . import Bitfield from . import CompletionCode from . import Optional from . import RemainingBytes @register_message_class class SetLanConfigurationParametersReq(Message): __cmdid__ = constants.CMDID_SET_LAN_CONFIGURATION_PARAMETERS __netfn__ = constants.NETFN_TRANSPORT __fields__ = ( Bitfield('command', 1, Bitfield.Bit('channel_number', 4, 0), Bitfield.ReservedBit(4, 0),), UnsignedInt('parameter_selector', 1), RemainingBytes('data'), ) @register_message_class class SetLanConfigurationParametersRsp(Message): __cmdid__ = constants.CMDID_SET_LAN_CONFIGURATION_PARAMETERS __netfn__ = constants.NETFN_TRANSPORT | 1 __fields__ = ( CompletionCode(), Optional(ByteArray('auxiliary', 4)) ) @register_message_class class GetLanConfigurationParametersReq(Message): __cmdid__ = constants.CMDID_GET_LAN_CONFIGURATION_PARAMETERS __netfn__ = constants.NETFN_TRANSPORT __fields__ = ( Bitfield('command', 1, Bitfield.Bit('channel_number', 4), Bitfield.ReservedBit(3, 0), Bitfield.Bit('get_parameter_revision_only', 1, 0),), UnsignedInt('parameter_selector', 1, 0), UnsignedInt('set_selector', 1, 0), UnsignedInt('block_selector', 1, 0), ) @register_message_class class GetLanConfigurationParametersRsp(Message): __cmdid__ = constants.CMDID_GET_LAN_CONFIGURATION_PARAMETERS __netfn__ = constants.NETFN_TRANSPORT | 1 __fields__ = ( CompletionCode(), UnsignedInt('parameter_revision', 1, 0), RemainingBytes('data'), ) python-ipmi-0.5.4/pyipmi/msgs/message.py000066400000000000000000000277341436677633700203160ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from array import array from . import constants from ..utils import ByteBuffer from ..errors import (CompletionCodeError, EncodingError, DecodingError, DescriptionError) class BaseField(object): def __init__(self, name, length, default=None): self.name = name self.length = length self.default = default def decode(self, obj, data): raise NotImplementedError() def encode(self, obj, data): if getattr(obj, self.name) is None: raise EncodingError('Field "%s" not set.' % self.name) raise NotImplementedError() def create(self): raise NotImplementedError() class ByteArray(BaseField): def __init__(self, name, length, default=None): BaseField.__init__(self, name, length) if default is not None: self.default = array('B', default) else: self.default = None def _length(self, obj): return self.length def encode(self, obj, data): a = getattr(obj, self.name) if len(a) != self._length(obj): raise EncodingError('Array must be exaclty %d bytes long ' '(but is %d long)' % (self._length(obj), len(a))) for i in range(self._length(obj)): data.push_unsigned_int(a[i], 1) def decode(self, obj, data): bytes = [] for i in range(self._length(obj)): bytes.append(data.pop_unsigned_int(1)) setattr(obj, self.name, array('B', bytes)) def create(self): if self.default is not None: return array('B', self.default) else: return array('B', self.length * '\x00') class VariableByteArray(ByteArray): """Array of bytes with variable length. The length is dynamically computed by a function. """ def __init__(self, name, length_func): ByteArray.__init__(self, name, None, None) self._length_func = length_func def _length(self, obj): return self._length_func(obj) def create(self): return None class UnsignedInt(BaseField): def encode(self, obj, data): value = getattr(obj, self.name) data.push_unsigned_int(value, self.length) def decode(self, obj, data): value = data.pop_unsigned_int(self.length) setattr(obj, self.name, value) def create(self): if self.default is not None: return self.default else: return 0 class String(BaseField): def encode(self, obj, data): value = getattr(obj, self.name) data.push_string(value) def decode(self, obj, data): value = data.pop_string(self.length) setattr(obj, self.name, value) def create(self): if self.default is not None: return self.default else: return '' class CompletionCode(UnsignedInt): def __init__(self, name='completion_code'): UnsignedInt.__init__(self, name, 1, None) def decode(self, obj, data): UnsignedInt.decode(self, obj, data) cc = getattr(obj, self.name) if cc != constants.CC_OK: raise CompletionCodeError(cc) class UnsignedIntMask(UnsignedInt): def __init__(self, name, length, mask, default=None): UnsignedInt.__init__(self, name, length, default) class Timestamp(UnsignedInt): def __init__(self, name): UnsignedInt.__init__(self, name, 4, None) class Conditional(object): def __init__(self, cond_fn, field): self._condition_fn = cond_fn self._field = field def __getattr__(self, name): return getattr(self._field, name) def encode(self, obj, data): if self._condition_fn(obj): self._field.encode(obj, data) def decode(self, obj, data): if self._condition_fn(obj): self._field.decode(obj, data) def create(self): return self._field.create() class Optional(object): def __init__(self, field): self._field = field def __getattr__(self, name): return getattr(self._field, name) def decode(self, obj, data): if len(data) > 0: self._field.decode(obj, data) else: setattr(obj, self._field.name, None) def encode(self, obj, data): if getattr(obj, self._field.name) is not None: self._field.encode(obj, data) def create(self): return None class RemainingBytes(BaseField): def __init__(self, name): BaseField.__init__(self, name, None) def encode(self, obj, data): a = getattr(obj, self.name) data.extend(a) def decode(self, obj, data): setattr(obj, self.name, array('B', data[:])) del data.array[:] def create(self): return array('B') class Bitfield(BaseField): class Bit(object): def __init__(self, name, width=1, default=None): self.name = name self._width = width self.default = default class ReservedBit(Bit): counter = 0 def __init__(self, width, default=0): Bitfield.Bit.__init__(self, 'reserved_bit_%d' % Bitfield.reserved_bit_counter, width, default) Bitfield.reserved_bit_counter += 1 class BitWrapper(object): def __init__(self, bits, length): self._bits = bits self._length = length for bit in bits: if hasattr(self, bit.name): raise DescriptionError('Bit with name "%s" already added' % bit.name) if bit.default is not None: setattr(self, bit.name, bit.default) else: setattr(self, bit.name, 0) def __str__(self): s = '[' for attr in dir(self): if attr.startswith('_'): continue s += '%s=%s, ' % (attr, getattr(self, attr)) s += ']' return s def __int__(self): return self._value def _get_value(self): value = 0 for bit in self._bits: bit_value = getattr(self, bit.name) if bit_value is None: bit_value = bit.default if bit_value is None: raise EncodingError('Bitfield "%s" not set.' % bit.name) value |= (bit_value & (2**bit._width - 1)) << bit.offset return value def _set_value(self, value): for bit in self._bits: tmp = (value >> bit.offset) & (2**bit._width - 1) setattr(self, bit.name, tmp) _value = property(_get_value, _set_value) reserved_bit_counter = 0 def __init__(self, name, length, *bits): BaseField.__init__(self, name, length) self._bits = bits self._precalc_offsets() def _precalc_offsets(self): offset = 0 for b in self._bits: b.offset = offset offset += b._width if offset != 8 * self.length: raise DescriptionError('Bit description does not match bitfield ' 'length') def encode(self, obj, data): wrapper = getattr(obj, self.name) value = wrapper._value for i in range(self.length): data.push_unsigned_int((value >> (8*i)) & 0xff, 1) def decode(self, obj, data): value = 0 for i in range(self.length): try: value |= data.pop_unsigned_int(1) << (8*i) except IndexError: raise DecodingError('Data too short for message') wrapper = getattr(obj, self.name) wrapper._value = value def create(self): return Bitfield.BitWrapper(self._bits, self.length) class GroupExtensionIdentifier(UnsignedInt): def __init__(self, name='picmg_identifier', value=None): UnsignedInt.__init__(self, name, 1, value) class EventMessageRevision(UnsignedInt): def __init__(self, value=None): UnsignedInt.__init__(self, 'event_message_rev', 1, value) class Message(object): RESERVED_FIELD_NAMES = ['cmdid', 'netfn', 'lun', 'group_extension'] __default_lun__ = 0 __group_extension__ = None __not_implemented__ = False def __init__(self, *args, **kwargs): """Message constructor with ([buf], [field=val,...]) prototype. Arguments: buf -- option message buffer to decode Optional keyword arguments corresponts to members to set (matching fields in self.__fields__, or 'data'). """ # create message fields if hasattr(self, '__fields__'): self._create_fields() # set default lun self.lun = self.__default_lun__ self.data = '' if args: self._decode(args[0]) else: for (name, value) in kwargs.items(): self._set_field(name, value) def _set_field(self, name, value): raise NotImplementedError() # TODO walk along the properties.. def __str__(self): return '{} [netfn={}, cmd={}, grp={}]'.format(type(self).__name__, self.netfn, self.cmdid, self.group_extension) def _create_fields(self): for field in self.__fields__: if field.name in self.RESERVED_FIELD_NAMES: raise DescriptionError('Field name "%s" is reserved' % field.name) if hasattr(self, field.name): raise DescriptionError('Field "%s" already added', field.name) setattr(self, field.name, field.create()) def _pack(self): """Pack the message and return an array.""" data = ByteBuffer() if not hasattr(self, '__fields__'): return data.array for field in self.__fields__: field.encode(self, data) return data.array def _encode(self): """Encode the message and return a bytestring.""" data = ByteBuffer() if not hasattr(self, '__fields__'): return data.tostring() for field in self.__fields__: field.encode(self, data) return data.tostring() def _decode(self, data): """Decode the bytestring message.""" if not hasattr(self, '__fields__'): return data = ByteBuffer(data) cc = None for field in self.__fields__: try: field.decode(self, data) except CompletionCodeError as e: # stop decoding on completion code != 0 cc = e.cc break if (cc is None or cc == 0) and len(data) > 0: raise DecodingError('Data has extra bytes') def _is_request(self): return self.__netfn__ & 1 == 0 def _is_response(self): return self.__netfn__ & 1 == 1 netfn = property(lambda s: s.__netfn__) cmdid = property(lambda s: s.__cmdid__) group_extension = property(lambda s: s.__group_extension__) def encode_message(msg): return msg._encode() def decode_message(msg, data): return msg._decode(data) def pack_message(msg): return msg._pack() python-ipmi-0.5.4/pyipmi/msgs/picmg.py000066400000000000000000000626131436677633700177640ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from . import constants from . import register_message_class from . import Message from . import UnsignedInt from . import UnsignedIntMask from . import Bitfield from . import CompletionCode from . import Conditional from . import RemainingBytes from . import Optional from . import GroupExtensionIdentifier PICMG_IDENTIFIER = 0x00 FRU_ACTIVATION_FRU_ACTIVATE = 0x1 FRU_ACTIVATION_FRU_DEACTIVATE = 0x0 LINK_INTERFACE_BASE = 0x0 LINK_INTERFACE_FABRIC = 0x1 LINK_INTERFACE_UPDATE_CHANNEL = 0x2 LINK_TYPE_BASE = 0x01 LINK_TYPE_ETHERNET_FABRIC = 0x02 LINK_TYPE_INFINIBAND_FABRIC = 0x03 LINK_TYPE_STARFABRIC_FABRIC = 0x04 LINK_TYPE_PCIEXPRESS_FABRIC = 0x05 LINK_TYPE_OEM0 = 0xf0 LINK_TYPE_OEM1 = 0xf1 LINK_TYPE_OEM2 = 0xf2 LINK_TYPE_OEM3 = 0xf3 LINK_TYPE_EXT_BASE0 = 0x00 LINK_TYPE_EXT_BASE1 = 0x01 LINK_SIGNALING_CLASS_BASIC = 0 LINK_SIGNALING_CLASS_10_3125_GBD = 3 # when link signaling class value is 0000b LINK_TYPE_EXT_ETHERNET_FIX1000_BX = 0x0 LINK_TYPE_EXT_ETHERNET_FIX10G_BX4 = 0x1 LINK_TYPE_EXT_ETHERNET_FCPI = 0x02 LINK_TYPE_EXT_ETHERNET_FIX1000_KX = 0x3 LINK_TYPE_EXT_ETHERNET_FIX10G_KX4 = 0x4 # when link signaling class value is 0011b LINK_TYPE_EXT_ETHERNET_FIX10G_KR = 0x0 LINK_TYPE_EXT_ETHERNET_FIX40G_KR4 = 0x1 LINK_TYPE_EXT_OEM_LINK_TYPE_EXT_0 = 0x00 LINK_FLAGS_LANE0 = 0x01 LINK_FLAGS_LANE0123 = 0x0f LINK_STATE_DISABLE = 0 LINK_STATE_ENABLE = 1 FRU_CONTROL_COLD_RESET = 0x00 FRU_CONTROL_WARM_RESET = 0x01 FRU_CONTROL_GRACEFUL_REBOOT = 0x02 FRU_CONTROL_ISSUE_DIAGNOSTIC_INTERRUPT = 0x03 FRU_CONTROL_QUIESCED = 0x04 LED_COLOR_BLUE = 0x01 LED_COLOR_RED = 0x02 LED_COLOR_GREEN = 0x03 LED_COLOR_AMBER = 0x04 LED_COLOR_ORANGE = 0x05 LED_COLOR_WHITE = 0x06 LED_FUNCTION_OFF = 0x00 LED_FUNCTION_BLINKING_RANGE = list(range(0x01, 0xfa)) LED_FUNCTION_LAMP_TEST = 0xfb LED_FUNCTION_ON = 0xff LED_STATE_LOCAL_CONTROL = 0 LED_STATE_OVERRIDE = 1 LED_STATE_LAMP_TEST = 2 class PicmgIdentifier(UnsignedInt): def __init__(self, name='picmg_identifier'): super(PicmgIdentifier, self).__init__(name, 1, PICMG_IDENTIFIER) class PicmgMessage(Message): __group_extension__ = PICMG_IDENTIFIER @register_message_class class GetPicmgPropertiesReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_PICMG_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetPicmgPropertiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_PICMG_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('extension_version', 1), UnsignedInt('max_fru_device_id', 1), UnsignedInt('fru_device_id', 1), ) @register_message_class class GetAddressInfoReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_ADDRESS_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetAddressInfoRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_ADDRESS_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('hardware_address', 1), UnsignedInt('ipmb_0_address', 1), UnsignedInt('ipmb_1_address', 1), Optional( UnsignedInt('fru_id', 1), ), Optional( UnsignedInt('site_id', 1), ), Optional( UnsignedInt('site_type', 1), ), Optional( UnsignedInt('carrier_number', 1), ), ) @register_message_class class GetShelfAddressInfoReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_SHELF_ADDRESS_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetShelfAddressInfoRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_SHELF_ADDRESS_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), RemainingBytes('shelf_address'), ) @register_message_class class FruControlReq(PicmgMessage): __cmdid__ = constants.CMDID_FRU_CONTROL __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('option', 1), ) @register_message_class class FruControlRsp(PicmgMessage): __cmdid__ = constants.CMDID_FRU_CONTROL __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), RemainingBytes('rsp_data'), ) @register_message_class class GetFruControlCapabilitiesReq(PicmgMessage): __cmdid__ = constants.CMDID_FRU_CONTROL_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetFruControlCapabilitiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_FRU_CONTROL_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('capabilities', 1, Bitfield.ReservedBit(1), Bitfield.Bit('warm_reset', 1), Bitfield.Bit('graceful_reboot', 1), Bitfield.Bit('diagnostic_interrupt', 1), Bitfield.ReservedBit(4),), ) @register_message_class class SetFruActivationPolicyReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_ACTIVATION_POLICY __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), Bitfield('mask', 1, Bitfield.Bit('activation_locked', 1, default=0), Bitfield.Bit('deactivation_locked', 1, default=0), Bitfield.ReservedBit(6),), Bitfield('set', 1, Bitfield.Bit('activation_locked', 1, default=0), Bitfield.Bit('deactivation_locked', 1, default=0), Bitfield.ReservedBit(6),), ) @register_message_class class SetFruActivationPolicyRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_ACTIVATION_POLICY __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetFruActivationPolicyReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_ACTIVATION_POLICY __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetFruActivationPolicyRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_ACTIVATION_POLICY __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('policy', 1, Bitfield.Bit('activation_locked', 1, default=0), Bitfield.Bit('deactivation_locked', 1, default=0), Bitfield.ReservedBit(6),), ) @register_message_class class SetFruActivationReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_ACTIVATION __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('control', 1), ) @register_message_class class SetFruActivationRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_ACTIVATION __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetDeviceLocatorRecordIdReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_DEVLOC_RECORD_ID __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetDeviceLocatorRecordIdRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_DEVLOC_RECORD_ID __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('record_id', 2), ) @register_message_class class GetFruLedPropertiesReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetFruLedPropertiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('general_status_led_properties', 1, Bitfield.Bit('blue_led', 1), Bitfield.Bit('led1', 1), Bitfield.Bit('led2', 1), Bitfield.Bit('led3', 1), Bitfield.ReservedBit(4),), UnsignedInt('application_specific_led_count', 1), ) @register_message_class class GetFruLedColorCapabilitiesReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_COLOR_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('led_id', 1), ) @register_message_class class GetFruLedColorCapabilitiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_COLOR_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('color_capabilities', 1, Bitfield.ReservedBit(1), Bitfield.Bit('blue', 1), Bitfield.Bit('red', 1), Bitfield.Bit('green', 1), Bitfield.Bit('amber', 1), Bitfield.Bit('orange', 1), Bitfield.Bit('white', 1), Bitfield.ReservedBit(1)), UnsignedIntMask('local_def_color', 1, 0x0f), UnsignedIntMask('override_def_color', 1, 0x0f), ) @register_message_class class GetPowerLevelReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_POWER_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('power_type', 1), ) @register_message_class class GetPowerLevelRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_POWER_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('properties', 1, Bitfield.Bit('power_level', 5, 0), Bitfield.ReservedBit(2, 0), Bitfield.Bit('dynamic_power_configuration', 1, 0),), UnsignedInt('delay_to_stable_power', 1), UnsignedInt('power_multiplier', 1), RemainingBytes('power_draw'), ) @register_message_class class GetFanSpeedPropertiesReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FAN_SPEED_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetFanSpeedPropertiesRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FAN_SPEED_PROPERTIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('minimum_speed_level', 1), UnsignedInt('maximum_speed_level', 1), UnsignedInt('normal_operation_level', 1), Bitfield('properties', 1, Bitfield.ReservedBit(7, 0), Bitfield.Bit('local_control_supported', 1),), ) @register_message_class class SetFanLevelReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_FAN_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('fan_level', 1), UnsignedInt('extra_byte', 1), ) @register_message_class class SetFanLevelRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_FAN_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetFanLevelReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FAN_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetFanLevelRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FAN_LEVEL __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('override_fan_level', 1), Optional( RemainingBytes('data'), ) ) @register_message_class class SetFruLedStateReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_LED_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('led_id', 1), UnsignedInt('led_function', 1), UnsignedInt('on_duration', 1), UnsignedIntMask('color', 1, 0x0f), ) @register_message_class class SetFruLedStateRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_FRU_LED_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetFruLedStateReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), UnsignedInt('led_id', 1), ) @register_message_class class GetFruLedStateRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_FRU_LED_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 def _cond_override(obj): return (obj.led_states.override_en == 1 or obj.led_states.lamp_test_en == 1) def _cond_lamp_test(obj): return obj.led_states.lamp_test_en == 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('led_states', 1, Bitfield.Bit('local_avail', 1), Bitfield.Bit('override_en', 1), Bitfield.Bit('lamp_test_en', 1), Bitfield.ReservedBit(5)), UnsignedInt('local_function', 1), UnsignedInt('local_on_duration', 1), UnsignedIntMask('local_color', 1, 0x0f), Conditional(_cond_override, UnsignedInt('override_function', 1)), Conditional(_cond_override, UnsignedInt('override_on_duration', 1)), Conditional(_cond_override, UnsignedIntMask('override_color', 1, 0x0f)), Conditional(_cond_lamp_test, UnsignedIntMask('lamp_test_duration', 1, 0x7f)), ) @register_message_class class SetPortStateReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_PORT_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('link_info', 4, Bitfield.Bit('channel', 6), Bitfield.Bit('interface', 2), Bitfield.Bit('port_0', 1), Bitfield.Bit('port_1', 1), Bitfield.Bit('port_2', 1), Bitfield.Bit('port_3', 1), Bitfield.Bit('type', 4), Bitfield.Bit('sig_class', 4, 0), Bitfield.Bit('type_extension', 4), Bitfield.Bit('grouping_id', 8, 0),), UnsignedInt('state', 1), ) @register_message_class class SetPortStateRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_PORT_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetPortStateReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_PORT_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('channel', 1, Bitfield.Bit('number', 6), Bitfield.Bit('interface', 2),), ) @register_message_class class GetPortStateRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_PORT_STATE __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), RemainingBytes('data'), ) @register_message_class class SetSignalingClassReq(PicmgMessage): __cmdid__ = constants.CMDID_SET_CHANNEL_SIGNALING_CLASS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('channel_info', 1, Bitfield.Bit('channel_number', 6, 0), Bitfield.Bit('interface', 2, 0),), Bitfield('channel_signaling', 1, Bitfield.Bit('class_capability', 4, 0), Bitfield.ReservedBit(4)), ) @register_message_class class SetSignalingClassRsp(PicmgMessage): __cmdid__ = constants.CMDID_SET_CHANNEL_SIGNALING_CLASS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetSignalingClassReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_CHANNEL_SIGNALING_CLASS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('channel_info', 1, Bitfield.Bit('channel_number', 6, 0), Bitfield.Bit('interface', 2, 0),), ) @register_message_class class GetSignalingClassRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_CHANNEL_SIGNALING_CLASS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('channel_info', 1, Bitfield.Bit('channel_number', 6, 0), Bitfield.Bit('interface', 2, 0),), Bitfield('channel_signaling', 1, Bitfield.Bit('class_capability', 4, 0), Bitfield.ReservedBit(4)), ) @register_message_class class GetLocationInformationReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_LOCATION_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('info', 1, Bitfield.Bit('carrier_number', 5, 0), Bitfield.ReservedBit(1), Bitfield.Bit('mcs', 2, 0),), UnsignedInt('site_number', 1), UnsignedInt('site_type', 1), ) @register_message_class class GetLocationInformationRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_LOCATION_INFO __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('slot_number', 1), UnsignedInt('tier_number', 1), Bitfield('info', 1, Bitfield.ReservedBit(5), Bitfield.Bit('carrier_orientation', 1, 0), Bitfield.Bit('tier_number', 1, 0), Bitfield.Bit('slot_number', 1, 0),), UnsignedInt('origin_x', 2), UnsignedInt('origin_y', 2), ) @register_message_class class SendPowerChannelControlReq(PicmgMessage): __cmdid__ = constants.CMDID_POWER_CHANNEL_CONTROL __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('channel', 1), UnsignedInt('control', 1), UnsignedInt('current_limit', 1), UnsignedInt('primary_pm', 1), UnsignedInt('backup_pm', 1), ) @register_message_class class SendPowerChannelControlRsp(PicmgMessage): __cmdid__ = constants.CMDID_POWER_CHANNEL_CONTROL __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetPowerChannelStatusReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_POWER_CHANNEL_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('starting_power_channel_number', 1), UnsignedInt('power_channel_count', 1), ) @register_message_class class GetPowerChannelStatusRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_POWER_CHANNEL_STATUS __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('max_power_channel_number', 1), Bitfield('global_status', 1, Bitfield.Bit('role', 1, 0), Bitfield.Bit('management_power_good', 1, 0), Bitfield.Bit('payload_power_good', 1, 0), Bitfield.Bit('unidentified_fault', 1, 0), Bitfield.ReservedBit(4),), RemainingBytes('data'), ) @register_message_class class SendPmHeartbeatReq(PicmgMessage): __cmdid__ = constants.CMDID_PM_HEARTBEAT __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('timeout', 1), Bitfield('ps1', 1, Bitfield.Bit('mch_1', 1, 0), Bitfield.Bit('mch_2', 1, 0), Bitfield.ReservedBit(6),), ) @register_message_class class SendPmHeartbeatRsp(PicmgMessage): __cmdid__ = constants.CMDID_PM_HEARTBEAT __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), ) @register_message_class class GetTelcoAlarmCapabilityReq(PicmgMessage): __cmdid__ = constants.CMDID_GET_TELCO_ALARM_CAPABILITY __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), UnsignedInt('fru_id', 1), ) @register_message_class class GetTelcoAlarmCapabilityRsp(PicmgMessage): __cmdid__ = constants.CMDID_GET_TELCO_ALARM_CAPABILITY __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('picmg_identifier', PICMG_IDENTIFIER), Bitfield('alarm_capabilities', 1, Bitfield.Bit('critical_alarm', 1, 0), Bitfield.Bit('major_alarm', 1, 0), Bitfield.Bit('minor_alarm', 1, 0), Bitfield.Bit('power_alarm', 1, 0), Bitfield.Bit('test_alarm', 1, 0), Bitfield.Bit('autonomous_alarm_cutoff', 1, 0), Bitfield.Bit('autonomous_minor_reset', 1, 0), Bitfield.Bit('autonomous_majorreset', 1, 0),), ) python-ipmi-0.5.4/pyipmi/msgs/registry.py000066400000000000000000000070701436677633700205310ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from functools import partial from ..errors import DescriptionError class MessageRegistry(object): def __init__(self): self.registry = dict() def register_class(self, cls): # some sanity checks # (1) class name has to end in Req or Rsp if cls.__name__[-3:] not in ('Req', 'Rsp'): raise DescriptionError('Class name has to end in Req or Rsp') # (2) mandantory fields for attr in ('__netfn__', '__cmdid__', '__default_lun__', '__group_extension__'): if not hasattr(cls, attr): raise DescriptionError('Class has to have attribute "%s"' % attr) # (3) netfn lsb has to be 0 for Req and 1 for Rsp if cls.__name__.endswith('Req') and cls.__netfn__ & 1 != 0: raise DescriptionError('LSB of NetFN of a Request must be 0') if cls.__name__.endswith('Rsp') and cls.__netfn__ & 1 != 1: raise DescriptionError('LSB of NetFN of a Request must be 1') # (4) must not be registered before if cls.__name__ in self.registry: raise DescriptionError('Message %s already registered' % cls.__name__) msg_id = (cls.__netfn__, cls.__cmdid__, cls.__group_extension__) if msg_id in self.registry: raise DescriptionError('Message (%d,%d,%d) already registered (%s)' % (msg_id[0], msg_id[1], msg_id[2], self.registry[msg_id])) # register name self.registry[cls.__name__] = cls # register (netfn, cmdid, group_extension) tuple self.registry[msg_id] = cls # register return cls def create(self, netfn, cmdid, group_extension, *args, **kwargs): return self.registry[(netfn, cmdid, group_extension)](*args, **kwargs) def create_response(self, req): return self.create(req.netfn + 1, req.cmdid, req.group_extension) def create_request_by_name(self, name, *args, **kwargs): return self.registry[name + "Req"](*args, **kwargs) def create_response_by_name(self, name, *args, **kwargs): return self.registry[name + "Rsp"](*args, **kwargs) DEFAULT_REGISTRY = MessageRegistry() register_message_class = partial(MessageRegistry.register_class, DEFAULT_REGISTRY) create_message = partial(MessageRegistry.create, DEFAULT_REGISTRY) create_response_message = partial(MessageRegistry.create_response, DEFAULT_REGISTRY) create_request_by_name = partial(MessageRegistry.create_request_by_name, DEFAULT_REGISTRY) create_response_by_name = partial(MessageRegistry.create_response_by_name, DEFAULT_REGISTRY) python-ipmi-0.5.4/pyipmi/msgs/sdr.py000066400000000000000000000150061436677633700174470ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from . import constants from . import register_message_class from . import Message from . import ByteArray from . import UnsignedInt from . import Timestamp from . import Bitfield from . import CompletionCode from . import RemainingBytes @register_message_class class GetSdrRepositoryInfoReq(Message): __cmdid__ = constants.CMDID_GET_SDR_REPOSITORY_INFO __netfn__ = constants.NETFN_STORAGE @register_message_class class GetSdrRepositoryInfoRsp(Message): __cmdid__ = constants.CMDID_GET_SDR_REPOSITORY_INFO __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('sdr_version', 1), UnsignedInt('record_count', 2), UnsignedInt('free_space', 2), Timestamp('most_recent_addition'), Timestamp('most_recent_erase'), Bitfield('support', 1, Bitfield.Bit('get_allocation_info', 1), Bitfield.Bit('reserve', 1), Bitfield.Bit('partial_add', 1), Bitfield.Bit('delete', 1), Bitfield.ReservedBit(1, 0), Bitfield.Bit('update_type', 2), Bitfield.Bit('overflow_flag', 1)), ) @register_message_class class GetSdrRepositoryAllocationInfoReq(Message): __cmdid__ = constants.CMDID_GET_SDR_REPOSITORY_ALLOCATION_INFO __netfn__ = constants.NETFN_STORAGE @register_message_class class GetSdrRepositoryAllocationInfoRsp(Message): __cmdid__ = constants.CMDID_GET_SDR_REPOSITORY_ALLOCATION_INFO __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('number_of_units', 2), UnsignedInt('unit_size', 2), UnsignedInt('free_units', 2), UnsignedInt('largest_free_block', 2), UnsignedInt('maximum_record_size', 1) ) @register_message_class class ReserveSdrRepositoryReq(Message): __cmdid__ = constants.CMDID_RESERVE_SDR_REPOSITORY __netfn__ = constants.NETFN_STORAGE @register_message_class class ReserveSdrRepositoryRsp(Message): __cmdid__ = constants.CMDID_RESERVE_SDR_REPOSITORY __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('reservation_id', 2), ) @register_message_class class GetSdrReq(Message): __cmdid__ = constants.CMDID_GET_SDR __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), UnsignedInt('record_id', 2), UnsignedInt('offset', 1), UnsignedInt('bytes_to_read', 1), ) @register_message_class class GetSdrRsp(Message): __cmdid__ = constants.CMDID_GET_SDR __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('next_record_id', 2), RemainingBytes('record_data'), ) @register_message_class class AddSdrReq(Message): __cmdid__ = constants.CMDID_ADD_SDR __netfn__ = constants.NETFN_STORAGE __fields__ = ( RemainingBytes('record_data'), ) @register_message_class class AddSdrRsp(Message): __cmdid__ = constants.CMDID_ADD_SDR __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('record_id', 2), ) @register_message_class class PartialAddSdrReq(Message): __cmdid__ = constants.CMDID_PARTIAL_ADD_SDR __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), UnsignedInt('record_id', 2), UnsignedInt('offset', 1), Bitfield('status', 1, Bitfield.Bit('in_progress', 4), Bitfield.ReservedBit(4, 0),), RemainingBytes('record_data'), ) @register_message_class class PartialAddSdrRsp(Message): __cmdid__ = constants.CMDID_PARTIAL_ADD_SDR __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('record_id', 2), ) @register_message_class class DeleteSdrReq(Message): __cmdid__ = constants.CMDID_DELETE_SDR __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), UnsignedInt('record_id', 2), ) @register_message_class class DeleteSdrRsp(Message): __cmdid__ = constants.CMDID_DELETE_SDR __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('record_id', 2), ) @register_message_class class ClearSdrRepositoryReq(Message): __cmdid__ = constants.CMDID_CLEAR_SDR_REPOSITORY __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), ByteArray('key', 3, default=b'CLR'), UnsignedInt('cmd', 1) ) @register_message_class class ClearSdrRepositoryRsp(Message): __cmdid__ = constants.CMDID_CLEAR_SDR_REPOSITORY __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), Bitfield('status', 1, Bitfield.Bit('erase_in_progress', 4), Bitfield.ReservedBit(4, 0),), ) @register_message_class class RunInitializationAgentReq(Message): __cmdid__ = constants.CMDID_RUN_INITIALIZATION_AGENT __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('cmd', 1), ) @register_message_class class RunInitializationAgentRsp(Message): __cmdid__ = constants.CMDID_RUN_INITIALIZATION_AGENT __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), Bitfield('status', 1, Bitfield.Bit('initialization_completed', 1), Bitfield.ReservedBit(7, 0),), ) python-ipmi-0.5.4/pyipmi/msgs/sel.py000066400000000000000000000135441436677633700174470ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import ByteArray from . import UnsignedInt from . import Timestamp from . import Bitfield from . import CompletionCode from . import RemainingBytes @register_message_class class GetSelInfoReq(Message): __cmdid__ = constants.CMDID_GET_SEL_INFO __netfn__ = constants.NETFN_STORAGE @register_message_class class GetSelInfoRsp(Message): __cmdid__ = constants.CMDID_GET_SEL_INFO __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('version', 1, default=0x51), UnsignedInt('entries', 2), UnsignedInt('free_bytes', 2), Timestamp('most_recent_addition'), Timestamp('most_recent_erase'), Bitfield('operation_support', 1, Bitfield.Bit('get_sel_allocation_info', 1), Bitfield.Bit('reserve_sel', 1), Bitfield.Bit('partial_add_sel_entry', 1), Bitfield.Bit('delete_sel', 1), Bitfield.ReservedBit(3), Bitfield.Bit('overflow_flag', 1)), ) @register_message_class class GetSelAllocationInfoReq(Message): __cmdid__ = constants.CMDID_GET_SEL_ALLOCATION_INFO __netfn__ = constants.NETFN_STORAGE @register_message_class class GetSelAllocationInfoRsp(Message): __cmdid__ = constants.CMDID_GET_SEL_ALLOCATION_INFO __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('possible_alloc_units', 2), UnsignedInt('alloc_unit_size', 2), UnsignedInt('free_alloc_units', 2), UnsignedInt('largest_free_block', 2), UnsignedInt('max_record_size', 1) ) @register_message_class class ReserveSelReq(Message): __cmdid__ = constants.CMDID_RESERVE_SEL __netfn__ = constants.NETFN_STORAGE @register_message_class class ReserveSelRsp(Message): __cmdid__ = constants.CMDID_RESERVE_SEL __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('reservation_id', 2) ) @register_message_class class GetSelEntryReq(Message): __cmdid__ = constants.CMDID_GET_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), UnsignedInt('record_id', 2), UnsignedInt('offset', 1), UnsignedInt('length', 1), ) @register_message_class class GetSelEntryRsp(Message): __cmdid__ = constants.CMDID_GET_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('next_record_id', 2), RemainingBytes('record_data'), ) @register_message_class class AddSelEntryReq(Message): __cmdid__ = constants.CMDID_ADD_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE __fields__ = ( ByteArray('record_data', 16) ) @register_message_class class AddSelEntryRsp(Message): __cmdid__ = constants.CMDID_ADD_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('record_id', 2) ) @register_message_class class DeleteSelEntryReq(Message): __cmdid__ = constants.CMDID_DELETE_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), UnsignedInt('record_id', 2) ) @register_message_class class DeleteSelEntryRsp(Message): __cmdid__ = constants.CMDID_DELETE_SEL_ENTRY __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), UnsignedInt('record_id', 2) ) @register_message_class class ClearSelReq(Message): __cmdid__ = constants.CMDID_CLEAR_SEL __netfn__ = constants.NETFN_STORAGE __fields__ = ( UnsignedInt('reservation_id', 2), ByteArray('key', 3, default=b'CLR'), UnsignedInt('cmd', 1) ) @register_message_class class ClearSelRsp(Message): __cmdid__ = constants.CMDID_CLEAR_SEL __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), Bitfield('status', 1, Bitfield.Bit('erase_in_progress', 4), Bitfield.ReservedBit(4),), ) @register_message_class class GetSelTimeReq(Message): __cmdid__ = constants.CMDID_GET_SEL_TIME __netfn__ = constants.NETFN_STORAGE @register_message_class class GetSelTimeRsp(Message): __cmdid__ = constants.CMDID_GET_SEL_TIME __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode(), Timestamp('timestamp') ) @register_message_class class SetSelTimeReq(Message): __cmdid__ = constants.CMDID_SET_SEL_TIME __netfn__ = constants.NETFN_STORAGE __fields__ = ( Timestamp('timestamp') ) @register_message_class class SetSelTimeRsp(Message): __cmdid__ = constants.CMDID_SET_SEL_TIME __netfn__ = constants.NETFN_STORAGE | 1 __fields__ = ( CompletionCode() ) python-ipmi-0.5.4/pyipmi/msgs/sensor.py000066400000000000000000000246471436677633700202030ustar00rootroot00000000000000from __future__ import absolute_import # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from . import constants from . import register_message_class from . import Message from . import UnsignedInt from . import Timestamp from . import Bitfield from . import CompletionCode from . import Optional from . import RemainingBytes from . import EventMessageRevision @register_message_class class GetDeviceSdrInfoReq(Message): __cmdid__ = constants.CMDID_GET_DEVICE_SDR_INFO __netfn__ = constants.NETFN_SENSOR_EVENT @register_message_class class GetDeviceSdrInfoRsp(Message): __cmdid__ = constants.CMDID_GET_DEVICE_SDR_INFO __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), UnsignedInt('number_of_sensors', 1), Bitfield('flags', 1, Bitfield.Bit('lun0_has_sensors', 1), Bitfield.Bit('lun1_has_sensors', 1), Bitfield.Bit('lun2_has_sensors', 1), Bitfield.Bit('lun3_has_sensors', 1), Bitfield.ReservedBit(3, 0), Bitfield.Bit('dynamic_population', 1)), Optional( Timestamp('sensor_population_change') ), ) @register_message_class class GetDeviceSdrReq(Message): __cmdid__ = constants.CMDID_GET_DEVICE_SDR __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('reservation_id', 2, 0x0000), UnsignedInt('record_id', 2), UnsignedInt('offset', 1), UnsignedInt('bytes_to_read', 1), ) @register_message_class class GetDeviceSdrRsp(Message): __cmdid__ = constants.CMDID_GET_DEVICE_SDR __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), UnsignedInt('next_record_id', 2), RemainingBytes('record_data'), ) @register_message_class class ReserveDeviceSdrRepositoryReq(Message): __cmdid__ = constants.CMDID_RESERVE_DEVICE_SDR_REPOSITORY __netfn__ = constants.NETFN_SENSOR_EVENT @register_message_class class ReserveDeviceSdrRepositoryRsp(Message): __cmdid__ = constants.CMDID_RESERVE_DEVICE_SDR_REPOSITORY __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), UnsignedInt('reservation_id', 2) ) @register_message_class class GetSensorThresholdsReq(Message): __cmdid__ = constants.CMDID_GET_SENSOR_THRESHOLD __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), ) @register_message_class class GetSensorThresholdsRsp(Message): __cmdid__ = constants.CMDID_GET_SENSOR_THRESHOLD __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), Bitfield('readable_mask', 1, Bitfield.Bit('lnc', 1, default=0), Bitfield.Bit('lcr', 1, default=0), Bitfield.Bit('lnr', 1, default=0), Bitfield.Bit('unc', 1, default=0), Bitfield.Bit('ucr', 1, default=0), Bitfield.Bit('unr', 1, default=0), Bitfield.ReservedBit(2, 0),), Bitfield('threshold', 6, Bitfield.Bit('lnc', 8, default=0), Bitfield.Bit('lcr', 8, default=0), Bitfield.Bit('lnr', 8, default=0), Bitfield.Bit('unc', 8, default=0), Bitfield.Bit('ucr', 8, default=0), Bitfield.Bit('unr', 8, default=0),), ) @register_message_class class SetSensorHysteresisReq(Message): __cmdid__ = constants.CMDID_SET_SENSOR_HYSTERESIS __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), UnsignedInt('reserved', 1, 0xff), UnsignedInt('positive_going_hysteresis', 1), UnsignedInt('negative_going_hysteresis', 1), ) @register_message_class class SetSensorHysteresisRsp(Message): __cmdid__ = constants.CMDID_SET_SENSOR_HYSTERESIS __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetSensorHysteresisReq(Message): __cmdid__ = constants.CMDID_GET_SENSOR_HYSTERESIS __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), UnsignedInt('reserved', 1, 0xff), ) @register_message_class class GetSensorHysteresisRsp(Message): __cmdid__ = constants.CMDID_GET_SENSOR_HYSTERESIS __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), UnsignedInt('positive_going_hysteresis', 1), UnsignedInt('negative_going_hysteresis', 1), ) @register_message_class class SetSensorThresholdsReq(Message): __cmdid__ = constants.CMDID_SET_SENSOR_THRESHOLD __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), Bitfield('set_mask', 1, Bitfield.Bit('lnc', 1, default=0), Bitfield.Bit('lcr', 1, default=0), Bitfield.Bit('lnr', 1, default=0), Bitfield.Bit('unc', 1, default=0), Bitfield.Bit('ucr', 1, default=0), Bitfield.Bit('unr', 1, default=0), Bitfield.ReservedBit(2, 0),), Bitfield('threshold', 6, Bitfield.Bit('lnc', 8, default=0), Bitfield.Bit('lcr', 8, default=0), Bitfield.Bit('lnr', 8, default=0), Bitfield.Bit('unc', 8, default=0), Bitfield.Bit('ucr', 8, default=0), Bitfield.Bit('unr', 8, default=0),), ) @register_message_class class SetSensorThresholdsRsp(Message): __cmdid__ = constants.CMDID_SET_SENSOR_THRESHOLD __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) @register_message_class class SetSensorEventEnableReq(Message): __cmdid__ = constants.CMDID_SET_SENSOR_EVENT_ENABLE __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), Bitfield('enable', 1, Bitfield.ReservedBit(4, 0), Bitfield.Bit('config', 2, 0), Bitfield.Bit('sensor_scanning', 1, 0), Bitfield.Bit('event_message', 1, 0),), Optional(UnsignedInt('byte3', 1)), Optional(UnsignedInt('byte4', 1)), Optional(UnsignedInt('byte5', 1)), Optional(UnsignedInt('byte6', 1)), ) @register_message_class class SetSensorEventEnableRsp(Message): __cmdid__ = constants.CMDID_SET_SENSOR_EVENT_ENABLE __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetSensorEventEnableReq(Message): __cmdid__ = constants.CMDID_GET_SENSOR_EVENT_ENABLE __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), ) @register_message_class class GetSensorEventEnableRsp(Message): __cmdid__ = constants.CMDID_GET_SENSOR_EVENT_ENABLE __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), Bitfield('enabled', 1, Bitfield.ReservedBit(6, 0), Bitfield.Bit('sensor_scanning', 1, 0), Bitfield.Bit('event_message', 1, 0),), Optional(UnsignedInt('byte3', 1)), Optional(UnsignedInt('byte4', 1)), Optional(UnsignedInt('byte5', 1)), Optional(UnsignedInt('byte6', 1)), ) @register_message_class class RearmSensorEventsReq(Message): __cmdid__ = constants.CMDID_RE_ARM_SENSOR __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), Bitfield('re_arm', 1, Bitfield.ReservedBit(7, 0), Bitfield.Bit('all_event_status', 1, 0),), UnsignedInt('re_arm_assertion_event', 2, 0), UnsignedInt('re_arm_deassertion_event', 2, 0), ) @register_message_class class RearmSensorEventsRsp(Message): __cmdid__ = constants.CMDID_RE_ARM_SENSOR __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) @register_message_class class GetSensorReadingReq(Message): __cmdid__ = constants.CMDID_GET_SENSOR_READING __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( UnsignedInt('sensor_number', 1), ) @register_message_class class GetSensorReadingRsp(Message): __cmdid__ = constants.CMDID_GET_SENSOR_READING __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), UnsignedInt('sensor_reading', 1), Bitfield('config', 1, Bitfield.ReservedBit(5, 0), Bitfield.Bit('initial_update_in_progress', 1, 0), Bitfield.Bit('sensor_scanning_disabled', 1, 0), Bitfield.Bit('event_message_disabled', 1, 0),), Optional(UnsignedInt('states1', 1)), Optional(UnsignedInt('states2', 1)), ) @register_message_class class PlatformEventReq(Message): __cmdid__ = constants.CMDID_PLATFORM_EVENT __netfn__ = constants.NETFN_SENSOR_EVENT __fields__ = ( EventMessageRevision(4), UnsignedInt('sensor_type', 1), UnsignedInt('sensor_number', 1), Bitfield('event_type', 1, Bitfield.Bit('type', 7, 0), Bitfield.Bit('dir', 1, 0),), RemainingBytes('event_data'), ) @register_message_class class PlatformEventRsp(Message): __cmdid__ = constants.CMDID_PLATFORM_EVENT __netfn__ = constants.NETFN_SENSOR_EVENT | 1 __fields__ = ( CompletionCode(), ) python-ipmi-0.5.4/pyipmi/msgs/session.py000066400000000000000000000000001436677633700203260ustar00rootroot00000000000000python-ipmi-0.5.4/pyipmi/msgs/vita.py000066400000000000000000000031341436677633700176210ustar00rootroot00000000000000# Copyright (c) 2021 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from . import constants from . import register_message_class from . import Message from . import CompletionCode from . import GroupExtensionIdentifier VITA_IDENTIFIER = 0x03 class VitaMessage(Message): __group_extension__ = VITA_IDENTIFIER @register_message_class class GetVsoCapabilitiesReq(VitaMessage): __cmdid__ = constants.CMDID_VITA_GET_VSO_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION __fields__ = ( GroupExtensionIdentifier('vita_identifier', VITA_IDENTIFIER), ) @register_message_class class GetVsoCapabilitiesRsp(VitaMessage): __cmdid__ = constants.CMDID_VITA_GET_VSO_CAPABILITIES __netfn__ = constants.NETFN_GROUP_EXTENSION | 1 __fields__ = ( CompletionCode(), GroupExtensionIdentifier('vita_identifier', VITA_IDENTIFIER), ) python-ipmi-0.5.4/pyipmi/picmg.py000066400000000000000000000456301436677633700170130ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from .errors import DecodingError, EncodingError from .msgs import create_request_by_name from .msgs import picmg from .utils import check_completion_code from .state import State from .msgs.picmg import \ FRU_CONTROL_COLD_RESET, FRU_CONTROL_WARM_RESET, \ FRU_CONTROL_GRACEFUL_REBOOT, FRU_CONTROL_ISSUE_DIAGNOSTIC_INTERRUPT, \ FRU_ACTIVATION_FRU_ACTIVATE, FRU_ACTIVATION_FRU_DEACTIVATE class Picmg(object): def get_picmg_properties(self): return self.send_message_with_name('GetPicmgProperties') def fru_control(self, fru_id, option): rsp = self.send_message_with_name('FruControl', fru_id=fru_id, option=option) return rsp.rsp_data def fru_control_cold_reset(self, fru_id=0): self.fru_control(fru_id, FRU_CONTROL_COLD_RESET) def fru_control_warm_reset(self, fru_id=0): self.fru_control(fru_id, FRU_CONTROL_WARM_RESET) def fru_control_graceful_reboot(self, fru_id=0): self.fru_control(fru_id, FRU_CONTROL_GRACEFUL_REBOOT) def fru_control_diagnostic_interrupt(self, fru_id=0): return self.fru_control(fru_id, FRU_CONTROL_ISSUE_DIAGNOSTIC_INTERRUPT) def get_power_level(self, fru_id, power_type): rsp = self.send_message_with_name('GetPowerLevel', fru_id=fru_id, power_type=power_type) return PowerLevel(rsp) def get_fan_speed_properties(self, fru_id): rsp = self.send_message_with_name('GetFanSpeedProperties', fru_id=fru_id) return FanSpeedProperties(rsp) def set_fan_level(self, fru_id, fan_level): self.send_message_with_name('SetFanLevel', fru_id=fru_id, fan_level=fan_level) def get_fan_level(self, fru_id): rsp = self.send_message_with_name('GetFanLevel', fru_id=fru_id) local_control_fan_level = None if rsp.data: local_control_fan_level = rsp.data[0] return (rsp.override_fan_level, local_control_fan_level) def get_led_state(self, fru_id, led_id): rsp = self.send_message_with_name('GetFruLedState', fru_id=fru_id, led_id=led_id) return LedState(rsp) def set_led_state(self, led): req = create_request_by_name('SetFruLedState') req = led.to_request(req) rsp = self.send_message(req) check_completion_code(rsp.completion_code) def _set_fru_activation(self, fru_id, control): self.send_message_with_name('SetFruActivation', fru_id=fru_id, control=control) def set_fru_activation(self, fru_id): self._set_fru_activation(fru_id, FRU_ACTIVATION_FRU_ACTIVATE) def set_fru_deactivation(self, fru_id): self._set_fru_activation(fru_id, FRU_ACTIVATION_FRU_DEACTIVATE) ACTIVATION_LOCK_SET = 0 ACTIVATION_LOCK_CLEAR = 1 DEACTIVATION_LOCK_SET = 2 DEACTIVATION_LOCK_CLEAR = 3 def set_fru_activation_policy(self, fru_id, ctrl): req = create_request_by_name('SetFruActivationPolicy') req.fru_id = fru_id if ctrl == self.ACTIVATION_LOCK_SET: req.mask.activation_locked = 1 req.set.activation_locked = 1 elif ctrl == self.ACTIVATION_LOCK_CLEAR: req.mask.activation_locked = 1 req.set.activation_locked = 0 elif ctrl == self.DEACTIVATION_LOCK_SET: req.mask.deactivation_locked = 1 req.set.deactivation_locked = 1 elif ctrl == self.DEACTIVATION_LOCK_CLEAR: req.mask.deactivation_locked = 1 req.set.deactivation_locked = 0 rsp = self.send_message(req) check_completion_code(rsp.completion_code) def set_fru_activation_lock(self, fru_id): self.set_fru_activation_policy(fru_id, self.ACTIVATION_LOCK_SET) def clear_fru_activation_lock(self, fru_id): self.set_fru_activation_policy(fru_id, self.ACTIVATION_LOCK_CLEAR) def set_fru_deactivation_lock(self, fru_id): self.set_fru_activation_policy(fru_id, self.DEACTIVATION_LOCK_SET) def clear_fru_deactivation_lock(self, fru_id): self.set_fru_activation_policy(fru_id, self.DEACTIVATION_LOCK_CLEAR) def set_port_state(self, link_descr, state): req = create_request_by_name('SetPortState') req.link_info.channel = link_descr.channel req.link_info.interface = link_descr.interface req.link_info.port_0 = (link_descr.link_flags >> 0) & 1 req.link_info.port_1 = (link_descr.link_flags >> 1) & 1 req.link_info.port_2 = (link_descr.link_flags >> 2) & 1 req.link_info.port_3 = (link_descr.link_flags >> 3) & 1 req.link_info.type = link_descr.type req.link_info.sig_class = link_descr.sig_class req.link_info.type_extension = link_descr.extension req.link_info.grouping_id = link_descr.grouping_id req.state = state rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_port_state(self, channel_number, channel_interface): req = create_request_by_name('GetPortState') req.channel.number = channel_number req.channel.interface = channel_interface rsp = self.send_message(req) check_completion_code(rsp.completion_code) if len(rsp.data) > 4: link = LinkDescriptor() link.channel = rsp.data[0] & 0x3F link.interface = rsp.data[0] >> 6 & 0x3 link.link_flags = rsp.data[1] & 0xf link.type = rsp.data[1] >> 4 & 0xf link.sig_class = rsp.data[2] & 0xf link.extension = rsp.data[2] >> 4 & 0xf link.grouping_id = rsp.data[3] state = rsp.data[4] return (link, state) def get_pm_global_status(self): rsp = self.send_message_with_name('GetPowerChannelStatus', starting_power_channel_number=1, power_channel_count=1) return GlobalStatus(rsp) def get_power_channel_status(self, start): rsp = self.send_message_with_name('GetPowerChannelStatus', starting_power_channel_number=start, power_channel_count=1) return PowerChannelStatus(rsp) def send_channel_power(self, channel, enable, current_limit, primary_pm=1, backup_pm=0): rsp = self.send_message_with_name('SendPowerChannelControl', channel=channel, control=5 if enable else 4, current_limit=int(current_limit * 10), primary_pm=primary_pm, backup_pm=backup_pm ) return rsp def send_pm_heartbeat(self): rsp = self.send_message_with_name('SendPmHeartbeat') return rsp def set_signaling_class(self, interface, channel, signaling_class): req = create_request_by_name('SetSignalingClass') req.channel_info.channel_number = channel req.channel_info.interface = interface req.channel_signaling.class_capability = signaling_class rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_signaling_class(self, interface, channel): req = create_request_by_name('GetSignalingClass') req.channel_info.channel_number = channel req.channel_info.interface = interface rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.channel_signaling.class_capability class LinkDescriptor(State): # TODO dont duplicate exports, import them instead INTERFACE_BASE = picmg.LINK_INTERFACE_BASE INTERFACE_FABRIC = picmg.LINK_INTERFACE_FABRIC INTERFACE_UPDATE_CHANNEL = picmg.LINK_INTERFACE_UPDATE_CHANNEL TYPE_BASE = picmg.LINK_TYPE_BASE TYPE_ETHERNET_FABRIC = picmg.LINK_TYPE_ETHERNET_FABRIC TYPE_INFINIBAND_FABRIC = picmg.LINK_TYPE_INFINIBAND_FABRIC TYPE_STARFABRIC_FABRIC = picmg.LINK_TYPE_STARFABRIC_FABRIC TYPE_PCIEXPRESS_FABRIC = picmg.LINK_TYPE_PCIEXPRESS_FABRIC TYPE_OEM0 = picmg.LINK_TYPE_OEM0 TYPE_OEM1 = picmg.LINK_TYPE_OEM1 TYPE_OEM2 = picmg.LINK_TYPE_OEM2 TYPE_OEM3 = picmg.LINK_TYPE_OEM3 TYPE_EXT_BASE0 = picmg.LINK_TYPE_EXT_BASE0 TYPE_EXT_BASE1 = picmg.LINK_TYPE_EXT_BASE1 SIGNALING_CLASS_BASIC = picmg.LINK_SIGNALING_CLASS_BASIC SIGNALING_CLASS_10_3125_GBD = picmg.LINK_SIGNALING_CLASS_10_3125_GBD TYPE_EXT_ETHERNET_FIX1000_BX = picmg.LINK_TYPE_EXT_ETHERNET_FIX1000_BX TYPE_EXT_ETHERNET_FIX10G_BX4 = picmg.LINK_TYPE_EXT_ETHERNET_FIX10G_BX4 TYPE_EXT_ETHERNET_FCPI = picmg.LINK_TYPE_EXT_ETHERNET_FCPI TYPE_EXT_ETHERNET_FIX1000_KX = picmg.LINK_TYPE_EXT_ETHERNET_FIX1000_KX TYPE_EXT_ETHERNET_FIX10G_KX4 = picmg.LINK_TYPE_EXT_ETHERNET_FIX10G_KX4 TYPE_EXT_ETHERNET_FIX10G_KR = picmg.LINK_TYPE_EXT_ETHERNET_FIX10G_KR TYPE_EXT_ETHERNET_FIX40G_KR4 = picmg.LINK_TYPE_EXT_ETHERNET_FIX40G_KR4 TYPE_EXT_OEM_LINK_TYPE_EXT_0 = picmg.LINK_TYPE_EXT_OEM_LINK_TYPE_EXT_0 FLAGS_LANE0 = picmg.LINK_FLAGS_LANE0 FLAGS_LANE0123 = picmg.LINK_FLAGS_LANE0123 STATE_DISABLE = picmg.LINK_STATE_DISABLE STATE_ENABLE = picmg.LINK_STATE_ENABLE __properties__ = [ # (property, description) ('channel', ''), ('interface', ''), ('link_flags', ''), ('type', ''), ('sig_class', ''), ('extension', ''), ('grouping_id', ''), ] INTERFACE_DESCR_STRING = [ # Interface, 'STRING' (INTERFACE_BASE, 'Base'), (INTERFACE_FABRIC, 'Fabric'), (INTERFACE_UPDATE_CHANNEL, 'Update Channel'), ] def get_interface_string(self, interf): for desc in self.INTERFACE_DESCR_STRING: if desc[0] == interf: return desc[1] return 'unknown' LINK_TYPE_DESCR_STRING = [ # Type, Extension, class, 'STRING' (TYPE_BASE, TYPE_EXT_BASE0, SIGNALING_CLASS_BASIC, '10/100/1000 BASE-T'), (TYPE_BASE, TYPE_EXT_BASE1, SIGNALING_CLASS_BASIC, '10/100 BASE-T ShMC Cross-connect'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX1000_BX, SIGNALING_CLASS_BASIC, 'Fixed 1000BASE-BX'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX10G_BX4, SIGNALING_CLASS_BASIC, 'Fixed 10GBASE-BX4 (XAUI)'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FCPI, SIGNALING_CLASS_BASIC, 'FC-PI'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX1000_KX, SIGNALING_CLASS_BASIC, 'Fixed 1000BASE-KX'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX10G_KX4, SIGNALING_CLASS_BASIC, 'Fixed 10GBASE-KX4'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX10G_KR, SIGNALING_CLASS_10_3125_GBD, 'Fixed 10GBASE-KR'), (TYPE_ETHERNET_FABRIC, TYPE_EXT_ETHERNET_FIX40G_KR4, SIGNALING_CLASS_10_3125_GBD, 'Fixed 40GBASE-KR4'), ] def get_link_type_string(self, link_type, ext, cls=0): for desc in self.LINK_TYPE_DESCR_STRING: if desc[0] == link_type and desc[1] == ext and desc[2] == cls: return desc[3] return 'unknown' class PowerLevel(State): def _from_response(self, rsp): self.dynamic_power_configuration = \ rsp.properties.dynamic_power_configuration self.power_level = rsp.properties.power_level self.delay_to_stable = rsp.delay_to_stable_power self.power_mulitplier = rsp.power_multiplier self.power_levels = rsp.power_draw class FanSpeedProperties(State): def _from_response(self, rsp): self.minimum_speed_level = rsp.minimum_speed_level self.maximum_speed_level = rsp.maximum_speed_level self.normal_operation_level = rsp.normal_operation_level self.local_control_supported = rsp.properties.local_control_supported class LedState(State): COLOR_BLUE = picmg.LED_COLOR_BLUE COLOR_RED = picmg.LED_COLOR_RED COLOR_GREEN = picmg.LED_COLOR_GREEN COLOR_AMBER = picmg.LED_COLOR_AMBER COLOR_ORANGE = picmg.LED_COLOR_ORANGE COLOR_WHITE = picmg.LED_COLOR_WHITE FUNCTION_OFF = 1 FUNCTION_BLINKING = 2 FUNCTION_ON = 3 FUNCTION_LAMP_TEST = 4 __properties__ = [ # (property, description) ('fru_id', ''), ('led_id', ''), ('local_state_available', ''), ('override_enabled', ''), ('lamp_test_enabled', ''), ('local_function', ''), ('local_off_duration', ''), ('local_on_duration', ''), ('local_color', ''), ('override_function', ''), ('override_off_duration', ''), ('override_on_duration', ''), ('override_color', ''), ('lamp_test_duration', ''), ] def __init__(self, rsp=None, fru_id=None, led_id=None, color=None, function=None): super(LedState, self).__init__(rsp) if fru_id is not None: self.fru_id = fru_id if led_id is not None: self.led_id = led_id if color is not None: self.override_color = color if function is not None: self.override_function = function def __str__(self): string = '[flags ' string += self.local_state_available and ' LOCAL_STATE' or '' string += self.override_enabled and ' OVR_EN' or '' string += self.lamp_test_enabled and ' LAMP_TEST_EN' or '' if not self.local_state_available and not self.override_enabled \ and not self.lamp_test_enabled: string += ' NONE' if self.local_state_available: string += ' local_function %s local_color %s' % ( self.local_function, self.local_color) if self.override_enabled: string += ' override_function %s override_color %s' % ( self.override_function, self.override_color) string += ']' return string def _from_response(self, res): self.local_state_available = bool(res.led_states.local_avail) self.override_enabled = bool(res.led_states.override_en) self.lamp_test_enabled = bool(res.led_states.lamp_test_en) if res.local_function == picmg.LED_FUNCTION_OFF: self.local_function = self.FUNCTION_OFF elif res.local_function == picmg.LED_FUNCTION_ON: self.local_function = self.FUNCTION_ON elif res.local_function in picmg.LED_FUNCTION_BLINKING_RANGE: self.local_function = self.FUNCTION_BLINKING self.local_off_duration = res.local_function * 10 if res.local_on_duration not in picmg.LED_FUNCTION_BLINKING_RANGE: raise DecodingError() self.local_on_duration = res.local_on_duration * 10 else: raise DecodingError() self.local_color = res.local_color if self.override_enabled: if res.override_function == picmg.LED_FUNCTION_OFF: self.override_function = self.FUNCTION_OFF elif res.override_function == picmg.LED_FUNCTION_ON: self.override_function = self.FUNCTION_ON elif res.override_function in picmg.LED_FUNCTION_BLINKING_RANGE: self.override_function = self.FUNCTION_BLINKING self.override_off_duration = res.local_function * 10 else: raise DecodingError() self.override_off_duration = res.override_on_duration * 10 self.override_color = res.override_color if self.lamp_test_enabled: self.lamp_test_duration = res.lamp_test_duration * 100 def to_request(self, req): req.fru_id = self.fru_id req.led_id = self.led_id req.color = self.override_color if self.override_function == self.FUNCTION_ON: req.led_function = picmg.LED_FUNCTION_ON req.on_duration = 0 elif self.override_function == self.FUNCTION_OFF: req.led_function = picmg.LED_FUNCTION_OFF req.on_duration = 0 elif self.override_function == self.FUNCTION_BLINKING: if self.override_off_duration not in \ picmg.LED_FUNCTION_BLINKING_RANGE: raise EncodingError() req.led_function = self.override_off_duration req.on_duration = self.override_on_duration elif self.override_function == self.FUNCTION_LAMP_TEST: req.led_function = picmg.LED_FUNCTION_LAMP_TEST req.on_duration = self.lamp_test_duration else: raise AssertionError() return req class GlobalStatus(State): __properties__ = [ # (property, description) ('role', ''), ('management_power_good', ''), ('payload_power_good', ''), ('unidentified_fault', ''), ] def _from_response(self, rsp): self.role = rsp.global_status.role self.management_power_good = \ bool(rsp.global_status.management_power_good) self.payload_power_good = \ bool(rsp.global_status.payload_power_good) self.unidentified_fault = \ bool(rsp.global_status.unidentified_fault) class PowerChannelStatus(State): __properties__ = [ # (property, description) ('present', ''), ('management_power', ''), ('management_power_overcurrent', ''), ('enable', ''), ('payload_power', ''), ('payload_power_overcurrent', ''), ('pwr_on', ''), ] def _from_response(self, rsp): data = rsp.data[0] self.present = (data >> 0) & 1 self.management_power = (data >> 1) & 1 self.management_power_overcurrent = (data >> 2) & 1 self.enable = (data >> 3) & 1 self.payload_power = (data >> 4) & 1 self.payload_power_overcurrent = (data >> 5) & 1 self.pwr_on = (data >> 6) & 1 python-ipmi-0.5.4/pyipmi/sdr.py000066400000000000000000000570641436677633700165100ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from __future__ import division import math from . import errors from .errors import DecodingError from .fields import SdrTypeLengthString from .utils import check_completion_code, ByteBuffer from .msgs import create_request_by_name from .helper import get_sdr_data_helper, clear_repository_helper from .helper import get_sdr_chunk_helper from .state import State SDR_TYPE_FULL_SENSOR_RECORD = 0x01 SDR_TYPE_COMPACT_SENSOR_RECORD = 0x02 SDR_TYPE_EVENT_ONLY_SENSOR_RECORD = 0x03 SDR_TYPE_ENTITY_ASSOCIATION_RECORD = 0x08 SDR_TYPE_FRU_DEVICE_LOCATOR_RECORD = 0x11 SDR_TYPE_MANAGEMENT_CONTROLLER_DEVICE_LOCATOR_RECORD = 0x12 SDR_TYPE_MANAGEMENT_CONTROLLER_CONFIRMATION_RECORD = 0x13 SDR_TYPE_BMC_MESSAGE_CHANNEL_INFO_RECORD = 0x14 SDR_TYPE_OEM_SENSOR_RECORD = 0xC0 GET_INITIALIZATION_AGENT_STATUS = 0 RUN_INITIALIZATION_AGENT = 1 L_LINEAR = 0 L_LN = 1 L_LOG = 2 L_LOG2 = 3 L_E = 4 L_EXP10 = 5 L_EXP2 = 6 L_1_X = 7 L_SQR = 8 L_CUBE = 9 L_SQRT = 10 L_CUBERT = 11 class Sdr(object): def __init__(self): pass def get_sdr_repository_info(self): return SdrRepositoryInfo( self.send_message_with_name('GetSdrRepositoryInfo')) def get_sdr_repository_allocation_info(self): return SdrRepositoryAllocationInfo( self.send_message_with_name('GetSdrRepositoryAllocationInfo')) def reserve_sdr_repository(self): rsp = self.send_message_with_name('ReserveSdrRepository') return rsp.reservation_id def _get_sdr_chunk(self, reservation_id, record_id, offset, length): req = create_request_by_name('GetSdr') req.reservation_id = reservation_id req.record_id = record_id req.offset = offset req.bytes_to_read = length rsp = get_sdr_chunk_helper(self.send_message, req, self.reserve_device_sdr_repository) return (rsp.next_record_id, rsp.record_data) def get_repository_sdr(self, record_id, reservation_id=None): (next_id, record_data) = get_sdr_data_helper( self.reserve_sdr_repository, self._get_sdr_chunk, record_id, reservation_id) return SdrCommon.from_data(record_data, next_id) def sdr_repository_entries(self): """A generator that returns the SDR list. The Generator starts with ID=0x0000 and ends when ID=0xffff is returned. """ reservation_id = self.reserve_sdr_repository() record_id = 0 while True: s = self.get_repository_sdr(record_id, reservation_id) yield s if s.next_id == 0xffff: break record_id = s.next_id def get_repository_sdr_list(self, reservation_id=None): """Return the complete SDR list.""" return list(self.sdr_repository_entries()) def partial_add_sdr(self, reservation_id, record_id, offset, progress, data): req = create_request_by_name('PartialAddSdr') req.reservation_id = reservation_id req.record_id = record_id req.offset = offset req.status.in_progress = progress req.data = data rsp = self.send_message(req) check_completion_code(rsp.completion_code) return rsp.record_id def delete_sdr(self, record_id): """Delete the sensor record specified by 'record_id'.""" reservation_id = self.reserve_device_sdr_repository() rsp = self.send_message_with_name('DeleteSdr', reservation_id=reservation_id, record_id=record_id) return rsp.record_id def _clear_sdr_repository(self, cmd, reservation_id): rsp = self.send_message_with_name('ClearSdrRepository', reservation_id=reservation_id, cmd=cmd) return rsp.status.erase_in_progress def clear_sdr_repository(self, retry=5): clear_repository_helper(self.reserve_sdr_repository, self._clear_sdr_repository, retry) def _run_initialization_agent(self, cmd): rsp = self.send_message_with_name('RunInitializationAgent', cmd=cmd) return rsp.status.initialization_completed def start_initialization_agent(self): self._run_initialization_agent(RUN_INITIALIZATION_AGENT) def get_initialization_agent_status(self): return self._run_initialization_agent(GET_INITIALIZATION_AGENT_STATUS) class SdrRepositoryInfo(State): def __init__(self, rsp): if rsp: self._from_response(rsp) def _from_response(self, rsp): self.sdr_version = rsp.sdr_version self.record_count = rsp.record_count self.free_space = rsp.free_space self.most_recent_addition = rsp.most_recent_addition self.support_get_allocation_info = rsp.support.get_allocation_info self.support_reserve = rsp.support.reserve self.support_partial_add = rsp.support.partial_add self.support_delete = rsp.support.delete self.support_update_type = rsp.support.update_type self.support_overflow_flag = rsp.support.overflow_flag class SdrRepositoryAllocationInfo(State): def __init__(self, rsp): if rsp: self._from_response(rsp) def _from_response(self, rsp): self.number_of_units = rsp.number_of_units self.unit_size = rsp.unit_size self.free_units = rsp.free_units self.largest_free_block = rsp.largest_free_block self.maximum_record_size = rsp.maximum_record_size class SdrCommon(object): def __init__(self, data=None, next_id=None): if data: self.data = data self._common_header(data) if hasattr(self, '_from_data'): self._from_data(data) if next_id: self.next_id = next_id def __str__(self): if hasattr(self, 'device_id_string'): s = '["%s"] [%s]' % \ (self.device_id_string, ' '.join(['%02x' % b for b in self.data])) else: s = '[%s]' % \ (' '.join(['%02x' % b for b in self.data])) return s def _common_header(self, data): buffer = ByteBuffer(data[:]) try: self.id = buffer.pop_unsigned_int(2) self.version = buffer.pop_unsigned_int(1) self.type = buffer.pop_unsigned_int(1) self.length = buffer.pop_unsigned_int(1) except IndexError: raise DecodingError('Invalid SDR length (%d)' % len(data)) def _common_record_key(self, buffer): self.owner_id = buffer.pop_unsigned_int(1) self.owner_lun = buffer.pop_unsigned_int(1) & 0x3 self.number = buffer.pop_unsigned_int(1) def _entity(self, buffer): self.entity_id = buffer.pop_unsigned_int(1) self.entity_instance = buffer.pop_unsigned_int(1) def _device_id_string(self, buffer): self.device_id_string_type = (buffer[0] & 0xc0) >> 4 self.device_id_string_length = buffer[0] & 0x3f field = SdrTypeLengthString(data=buffer[0:1+self.device_id_string_length]) self.device_id_string = field.string # self.device_id_string = \ # buffer.pop_string(self.device_id_string_length & 0x3f) @staticmethod def from_data(data, next_id=None): sdr_type = data[3] cls = { SDR_TYPE_FULL_SENSOR_RECORD: SdrFullSensorRecord, SDR_TYPE_COMPACT_SENSOR_RECORD: SdrCompactSensorRecord, SDR_TYPE_EVENT_ONLY_SENSOR_RECORD: SdrEventOnlySensorRecord, SDR_TYPE_FRU_DEVICE_LOCATOR_RECORD: SdrFruDeviceLocator, SDR_TYPE_MANAGEMENT_CONTROLLER_DEVICE_LOCATOR_RECORD: SdrManagementControllerDeviceLocator, SDR_TYPE_MANAGEMENT_CONTROLLER_CONFIRMATION_RECORD: SdrManagementControllerConfirmationRecord, SDR_TYPE_OEM_SENSOR_RECORD: SdrOEMSensorRecord, }.get(sdr_type, SdrUnknownSensorRecord) return cls(data, next_id) ### # SDR type 0x01 ################################################## class SdrFullSensorRecord(SdrCommon): DATA_FMT_UNSIGNED = 0 DATA_FMT_1S_COMPLEMENT = 1 DATA_FMT_2S_COMPLEMENT = 2 DATA_FMT_NONE = 3 def __init__(self, data=None, next_id=None): super(SdrFullSensorRecord, self).__init__(data, next_id) def __str__(self): s = '["%s"] [%s:%s] [%s]' \ % (self.device_id_string, self.entity_id, self.entity_instance, ' '.join(['%02x' % b for b in self.data])) return s def convert_sensor_raw_to_value(self, raw): if raw is None: return None fmt = self.analog_data_format if (fmt == self.DATA_FMT_1S_COMPLEMENT): if raw & 0x80: raw = -((raw & 0x7f) ^ 0x7f) elif (fmt == self.DATA_FMT_2S_COMPLEMENT): if raw & 0x80: raw = -((raw & 0x7f) ^ 0x7f) - 1 raw = float(raw) return self.lin((self.m * raw + (self.b * 10**self.k1)) * 10**self.k2) def convert_sensor_value_to_raw(self, value): linearization = self.linearization & 0x7f if linearization is not L_LINEAR: raise NotImplementedError() raw = ((float(value) * 10**(-1 * self.k2)) / self.m) - (self.b * 10**self.k1) fmt = self.analog_data_format if (fmt == self.DATA_FMT_1S_COMPLEMENT): if value < 0: raise NotImplementedError() elif (fmt == self.DATA_FMT_2S_COMPLEMENT): if value < 0: raise NotImplementedError() raw = int(round(raw)) if raw > 0xff: raise ValueError() return raw @property def lin(self): try: return { L_LN: math.log, L_LOG: lambda x: math.log(x, 10), L_LOG2: lambda x: math.log(x, 2), L_E: math.exp, L_EXP10: lambda x: math.pow(10, x), L_EXP2: lambda x: math.pow(2, x), L_1_X: lambda x: 1.0 / x, L_SQR: lambda x: math.pow(x, 2), L_CUBE: lambda x: math.pow(x, 3), L_SQRT: math.sqrt, L_CUBERT: lambda x: math.pow(x, 1.0/3), L_LINEAR: lambda x: x, }[self.linearization & 0x7f] except KeyError: raise errors.DecodingError('unknown linearization %d' % (self.linearization & 0x7f)) @staticmethod def _convert_complement(value, size): if (value & (1 << (size - 1))): value = -(1 << size) + value return value def _decode_capabilities(self, capabilities): self.capabilities = [] # ignore sensor if capabilities & 0x80: self.capabilities.append('ignore_sensor') # sensor auto re-arm support if capabilities & 0x40: self.capabilities.append('auto_rearm') # sensor hysteresis support HYSTERESIS_MASK = 0x30 HYSTERESIS_IS_NOT_SUPPORTED = 0x00 HYSTERESIS_IS_READABLE = 0x10 HYSTERESIS_IS_READ_AND_SETTABLE = 0x20 HYSTERESIS_IS_FIXED = 0x30 if capabilities & HYSTERESIS_MASK == HYSTERESIS_IS_NOT_SUPPORTED: self.capabilities.append('hysteresis_not_supported') elif capabilities & HYSTERESIS_MASK == HYSTERESIS_IS_READABLE: self.capabilities.append('hysteresis_readable') elif capabilities & HYSTERESIS_MASK == HYSTERESIS_IS_READ_AND_SETTABLE: self.capabilities.append('hysteresis_read_and_setable') elif capabilities & HYSTERESIS_MASK == HYSTERESIS_IS_FIXED: self.capabilities.append('hysteresis_fixed') # sensor threshold support THRESHOLD_MASK = 0x0C THRESHOLD_IS_NOT_SUPPORTED = 0x00 THRESHOLD_IS_READABLE = 0x08 THRESHOLD_IS_READ_AND_SETTABLE = 0x04 THRESHOLD_IS_FIXED = 0x0C if capabilities & THRESHOLD_MASK == THRESHOLD_IS_NOT_SUPPORTED: self.capabilities.append('threshold_not_supported') elif capabilities & THRESHOLD_MASK == THRESHOLD_IS_READABLE: self.capabilities.append('threshold_readable') elif capabilities & THRESHOLD_MASK == THRESHOLD_IS_READ_AND_SETTABLE: self.capabilities.append('threshold_read_and_setable') elif capabilities & THRESHOLD_MASK == THRESHOLD_IS_FIXED: self.capabilities.append('threshold_fixed') # sensor event message control support if (capabilities & 0x03) == 0: pass if (capabilities & 0x03) == 1: pass if (capabilities & 0x03) == 2: pass if (capabilities & 0x03) == 3: pass def _from_data(self, data): buffer = ByteBuffer(data[5:]) # record key bytes self._common_record_key(buffer.pop_slice(3)) # record body bytes self._entity(buffer.pop_slice(2)) # byte 11 initialization = buffer.pop_unsigned_int(1) self.initialization = [] if initialization & 0x40: self.initialization.append('scanning') if initialization & 0x20: self.initialization.append('events') if initialization & 0x10: self.initialization.append('thresholds') if initialization & 0x08: self.initialization.append('hysteresis') if initialization & 0x04: self.initialization.append('type') if initialization & 0x02: self.initialization.append('default_event_generation') if initialization & 0x01: self.initialization.append('default_scanning') # byte 12 - sensor capabilities self._decode_capabilities(buffer.pop_unsigned_int(1)) self.sensor_type_code = buffer.pop_unsigned_int(1) self.event_reading_type_code = buffer.pop_unsigned_int(1) self.assertion_mask = buffer.pop_unsigned_int(2) self.deassertion_mask = buffer.pop_unsigned_int(2) self.discrete_reading_mask = buffer.pop_unsigned_int(2) # byte 21, 22, 23 self.units_1 = buffer.pop_unsigned_int(1) self.units_2 = buffer.pop_unsigned_int(1) self.units_3 = buffer.pop_unsigned_int(1) self.analog_data_format = (self.units_1 >> 6) & 0x3 self.rate_unit = (self.units_1 >> 3) >> 0x7 self.modifier_unit = (self.units_1 >> 1) & 0x2 self.percentage = self.units_1 & 0x1 # byte 24 self.linearization = buffer.pop_unsigned_int(1) & 0x7f # byte 25, 26 m = buffer.pop_unsigned_int(1) m_tol = buffer.pop_unsigned_int(1) self.m = (m & 0xff) | ((m_tol & 0xc0) << 2) # NAC: Bug fix. Upstream did not properly account for # 'M' being a twos complement value. self.m = self._convert_complement(self.m, 10) self.tolerance = (m_tol & 0x3f) # byte 27, 28, 29 b = buffer.pop_unsigned_int(1) b_acc = buffer.pop_unsigned_int(1) acc_accexp = buffer.pop_unsigned_int(1) self.b = (b & 0xff) | ((b_acc & 0xc0) << 2) self.b = self._convert_complement(self.b, 10) self.accuracy = (b_acc & 0x3f) | ((acc_accexp & 0xf0) << 4) self.accuracy_exp = (acc_accexp & 0x0c) >> 2 # byte 30 rexp_bexp = buffer.pop_unsigned_int(1) self.k2 = (rexp_bexp & 0xf0) >> 4 # convert 2s complement self.k2 = self._convert_complement(self.k2, 4) self.k1 = rexp_bexp & 0x0f # convert 2s complement self.k1 = self._convert_complement(self.k1, 4) # byte 31 analog_characteristics = buffer.pop_unsigned_int(1) self.analog_characteristic = [] if analog_characteristics & 0x01: self.analog_characteristic.append('nominal_reading') if analog_characteristics & 0x02: self.analog_characteristic.append('normal_max') if analog_characteristics & 0x04: self.analog_characteristic.append('normal_min') self.nominal_reading = buffer.pop_unsigned_int(1) self.normal_maximum = buffer.pop_unsigned_int(1) self.normal_minimum = buffer.pop_unsigned_int(1) self.sensor_maximum_reading = buffer.pop_unsigned_int(1) self.sensor_minimum_reading = buffer.pop_unsigned_int(1) self.threshold = {} self.threshold['unr'] = buffer.pop_unsigned_int(1) self.threshold['ucr'] = buffer.pop_unsigned_int(1) self.threshold['unc'] = buffer.pop_unsigned_int(1) self.threshold['lnr'] = buffer.pop_unsigned_int(1) self.threshold['lcr'] = buffer.pop_unsigned_int(1) self.threshold['lnc'] = buffer.pop_unsigned_int(1) self.hysteresis = {} self.hysteresis['positive_going'] = buffer.pop_unsigned_int(1) self.hysteresis['negative_going'] = buffer.pop_unsigned_int(1) self.reserved = buffer.pop_unsigned_int(2) self.oem = buffer.pop_unsigned_int(1) self._device_id_string(buffer) ### # SDR type 0x02 ################################################## class SdrCompactSensorRecord(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrCompactSensorRecord, self).__init__(data, next_id) def __str__(self): s = '["%s"] [%s]' \ % (self.device_id_string, ' '.join(['%02x' % b for b in self.data])) return s def _from_data(self, data): buffer = ByteBuffer(data[5:]) # record key bytes self._common_record_key(buffer.pop_slice(3)) # record body bytes self._entity(buffer.pop_slice(2)) self.sensor_initialization = buffer.pop_unsigned_int(1) self.capabilities = buffer.pop_unsigned_int(1) self.sensor_type_code = buffer.pop_unsigned_int(1) self.event_reading_type_code = buffer.pop_unsigned_int(1) self.assertion_mask = buffer.pop_unsigned_int(2) self.deassertion_mask = buffer.pop_unsigned_int(2) self.discrete_reading_mask = buffer.pop_unsigned_int(2) self.units_1 = buffer.pop_unsigned_int(1) self.units_2 = buffer.pop_unsigned_int(1) self.units_3 = buffer.pop_unsigned_int(1) self.record_sharing = buffer.pop_unsigned_int(2) self.positive_going_hysteresis = buffer.pop_unsigned_int(1) self.negative_going_hysteresis = buffer.pop_unsigned_int(1) self.reserved = buffer.pop_unsigned_int(3) self.oem = buffer.pop_unsigned_int(1) self._device_id_string(buffer) ### # SDR type 0x03 ################################################## class SdrEventOnlySensorRecord(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrEventOnlySensorRecord, self).__init__(data, next_id) def __str__(self): return 'Not supported yet.' def _from_data(self, data): buffer = ByteBuffer(data[5:]) # record key bytes self._common_record_key(buffer.pop_slice(3)) # record body bytes self._entity(buffer.pop_slice(2)) self.sensor_type = buffer.pop_unsigned_int(1) self.event_reading_type_code = buffer.pop_unsigned_int(1) self.record_sharing = buffer.pop_unsigned_int(2) self.reserved = buffer.pop_unsigned_int(1) self.oem = buffer.pop_unsigned_int(1) self._device_id_string(buffer) ### # SDR type 0x11 ################################################## class SdrFruDeviceLocator(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrFruDeviceLocator, self).__init__(data, next_id) def __str__(self): s = '["%s"] [%s]' \ % (self.device_id_string, ' '.join(['%02x' % b for b in self.data])) return s def _from_data(self, data): buffer = ByteBuffer(data[5:]) self.device_access_address = buffer.pop_unsigned_int(1) >> 1 self.fru_device_id = buffer.pop_unsigned_int(1) self.logical_physical = buffer.pop_unsigned_int(1) self.channel_number = buffer.pop_unsigned_int(1) self.reserved = buffer.pop_unsigned_int(1) self.device_type = buffer.pop_unsigned_int(1) self.device_type_modifier = buffer.pop_unsigned_int(1) self._entity(buffer.pop_slice(2)) self.oem = buffer.pop_unsigned_int(1) self._device_id_string(buffer) ### # SDR type 0x12 ################################################## class SdrManagementControllerDeviceLocator(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrManagementControllerDeviceLocator, self).__init__( data, next_id) def __str__(self): s = '["%s"] [%s]' \ % (self.device_id_string, ' '.join(['%02x' % b for b in self.data])) return s def _from_data(self, data): buffer = ByteBuffer(data[5:]) self.device_slave_address = buffer.pop_unsigned_int(1) >> 1 self.channel_number = buffer.pop_unsigned_int(1) & 0xf self.power_state_notification = buffer.pop_unsigned_int(1) self.global_initialization = 0 self.device_capabilities = buffer.pop_unsigned_int(1) self.reserved = buffer.pop_unsigned_int(3) self._entity(buffer.pop_slice(2)) self.oem = buffer.pop_unsigned_int(1) self._device_id_string(buffer) ### # SDR type 0x13 ################################################## class SdrManagementControllerConfirmationRecord(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrManagementControllerConfirmationRecord, self).__init__( data, next_id) def _from_data(self, data): buffer = ByteBuffer(data[5:]) self.device_slave_address = buffer.pop_unsigned_int(1) >> 1 self.device_id = buffer.pop_unsigned_int(1) self.channel_number = buffer.pop_unsigned_int(1) self.firmware_revision_1 = buffer.pop_unsigned_int(1) self.firmware_revision_2 = buffer.pop_unsigned_int(1) self.ipmi_version = buffer.pop_unsigned_int(1) self.manufacturer_id = buffer.pop_unsigned_int(3) & 0xfffff self.product_id = buffer.pop_unsigned_int(2) self.device_guid = buffer.pop_unsigned_int(16) ### # SDR type 0xC0 ################################################## class SdrOEMSensorRecord(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrOEMSensorRecord, self).__init__(data, next_id) def __str__(self): return 'Not supported yet.' def _from_data(self, data): buffer = ByteBuffer(data[5:]) # record key bytes self._common_record_key(buffer.pop_slice(3)) # Any SDR type not known or not implemented class SdrUnknownSensorRecord(SdrCommon): def __init__(self, data=None, next_id=None): super(SdrUnknownSensorRecord, self).__init__(data, next_id) def __str__(self): return 'Not supported yet.' python-ipmi-0.5.4/pyipmi/sel.py000066400000000000000000000177321436677633700165010ustar00rootroot00000000000000 # Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from array import array from .errors import CompletionCodeError, DecodingError from .utils import check_completion_code, ByteBuffer from .msgs import create_request_by_name from .msgs import constants from .event import EVENT_ASSERTION, EVENT_DEASSERTION from .helper import clear_repository_helper from .state import State class Sel(object): def get_sel_entries_count(self): info = SelInfo(self.send_message_with_name('GetSelInfo')) return info.entries def get_sel_reservation_id(self): rsp = self.send_message_with_name('ReserveSel') return rsp.reservation_id def _clear_sel(self, cmd, reservation): rsp = self.send_message_with_name('ClearSel', reservation_id=reservation, cmd=cmd) return rsp.status.erase_in_progress def clear_sel(self, retry=5): clear_repository_helper(self.get_sel_reservation_id, self._clear_sel, retry) def delete_sel_entry(self, record_id, reservation=0): rsp = self.send_message_with_name('DeleteSelEntry', reservation_id=reservation, record_id=record_id) return rsp.record_id def get_and_clear_sel_entry(self, record_id): """Atomically gets and clears the specified SEL record""" while True: reservation = self.get_sel_reservation_id() try: sel_entry, _ = self.get_sel_entry(record_id, reservation) except CompletionCodeError as e: if e.cc == constants.CC_RES_CANCELED: continue else: raise try: self.delete_sel_entry(record_id, reservation) except CompletionCodeError as e: if e.cc == constants.CC_RES_CANCELED: continue else: raise return sel_entry def get_sel_entry(self, record_id, reservation=0): ENTIRE_RECORD = 0xff req = create_request_by_name('GetSelEntry') req.reservation_id = reservation req.record_id = record_id req.offset = 0 self.max_req_len = ENTIRE_RECORD record_data = ByteBuffer() while True: req.length = self.max_req_len if (self.max_req_len != 0xff and (req.offset + req.length) > 16): req.length = 16 - req.offset rsp = self.send_message(req) if rsp.completion_code == constants.CC_CANT_RET_NUM_REQ_BYTES: if self.max_req_len == 0xff: self.max_req_len = 16 else: self.max_req_len -= 1 continue else: check_completion_code(rsp.completion_code) record_data.extend(rsp.record_data) req.offset = len(record_data) if len(record_data) >= 16: break return (SelEntry(record_data), rsp.next_record_id) def sel_entries(self): """Generator which returns all SEL entries.""" START_SEL_RECORD_ID = 0 END_SEL_RECORD_ID = 0xffff if self.get_sel_entries_count() == 0: return reservation_id = self.get_sel_reservation_id() next_record_id = START_SEL_RECORD_ID while True: (sel_entry, next_record_id) = self.get_sel_entry(next_record_id, reservation_id) yield sel_entry if next_record_id == END_SEL_RECORD_ID: break def get_sel_entries(self): """Return all SEL entries as a list.""" return list(self.sel_entries()) class SelInfo(State): def _from_response(self, rsp): self.version = rsp.version self.entries = rsp.entries self.free_bytes = rsp.free_bytes self.most_recent_addition = rsp.most_recent_addition self.most_recent_erase = rsp.most_recent_erase self.operation_support = [] if rsp.operation_support.get_sel_allocation_info: self.operation_support.append('get_sel_allocation_info') if rsp.operation_support.reserve_sel: self.operation_support.append('reserve_sel') if rsp.operation_support.partial_add_sel_entry: self.operation_support.append('partial_add_sel_entry') if rsp.operation_support.delete_sel: self.operation_support.append('delete_sel') if rsp.operation_support.overflow_flag: self.operation_support.append('overflow_flag') class SelEntry(State): TYPE_SYSTEM_EVENT = 0x02 TYPE_OEM_TIMESTAMPED_RANGE = list(range(0xc0, 0xe0)) TYPE_OEM_NON_TIMESTAMPED_RANGE = list(range(0xe0, 0x100)) def __str__(self): raw = '[%s]' % (' '.join(['0x%02x' % b for b in self.data])) string = [] string.append('SEL Record ID 0x%04x' % self.record_id) string.append(' Raw: %s' % raw) string.append(' Type: %d' % self.type) string.append(' Timestamp: %d' % self.timestamp) string.append(' Generator: %d' % self.generator_id) string.append(' EvM rev: %d' % self.evm_rev) string.append(' Sensor Type: 0x%02x' % self.sensor_type) string.append(' Sensor Number: %d' % self.sensor_number) string.append(' Event Direction: %d' % self.event_direction) string.append(' Event Type: 0x%02x' % self.event_type) string.append(' Event Data: %s' % array('B', self.event_data).tolist()) return "\n".join(string) @staticmethod def type_to_string(entry_type): string = None if entry_type == SelEntry.TYPE_SYSTEM_EVENT: string = 'System Event' elif entry_type in SelEntry.TYPE_OEM_TIMESTAMPED_RANGE: string = 'OEM timestamped (0x%02x)' % entry_type elif entry_type in SelEntry.TYPE_OEM_NON_TIMESTAMPED_RANGE: string = 'OEM non-timestamped (0x%02x)' % entry_type return string def _from_response(self, data): if len(data) != 16: raise DecodingError('Invalid SEL record length (%d)' % len(data)) self.data = data # pop will change data, therefore copy it buffer = ByteBuffer(data) self.record_id = buffer.pop_unsigned_int(2) self.type = buffer.pop_unsigned_int(1) if (self.type != self.TYPE_SYSTEM_EVENT and self.type not in self.TYPE_OEM_TIMESTAMPED_RANGE and self.type not in self.TYPE_OEM_NON_TIMESTAMPED_RANGE): raise DecodingError('Unknown SEL type (0x%02x)' % self.type) self.timestamp = buffer.pop_unsigned_int(4) self.generator_id = buffer.pop_unsigned_int(2) self.evm_rev = buffer.pop_unsigned_int(1) self.sensor_type = buffer.pop_unsigned_int(1) self.sensor_number = buffer.pop_unsigned_int(1) event_desc = buffer.pop_unsigned_int(1) if event_desc & 0x80: self.event_direction = EVENT_DEASSERTION else: self.event_direction = EVENT_ASSERTION self.event_type = event_desc & 0x7f self.event_data = [buffer.pop_unsigned_int(1) for _ in range(3)] python-ipmi-0.5.4/pyipmi/sensor.py000066400000000000000000000200061436677633700172130ustar00rootroot00000000000000# cOPYRIGht (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import from .utils import check_completion_code from .msgs import create_request_by_name from .helper import get_sdr_data_helper, get_sdr_chunk_helper from . import sdr # THRESHOLD BASED STATES EVENT_READING_TYPE_CODE_THRESHOLD = 0x01 # DMI-based "Usage States" STATES EVENT_READING_TYPE_CODE_DISCRETE = 0x02 # DIGITAL/DISCRETE EVENT STATES EVENT_READING_TYPE_CODE_STATE = 0x03 EVENT_READING_TYPE_CODE_PREDICTIVE_FAILURE = 0x04 EVENT_READING_TYPE_CODE_LIMIT = 0x05 EVENT_READING_TYPE_CODE_PERFORMANCE = 0x06 EVENT_READING_TYPE_SENSOR_SPECIFIC = 0x6f # Sensor Types SENSOR_TYPE_TEMPERATURE = 0x01 SENSOR_TYPE_VOLTAGE = 0x02 SENSOR_TYPE_CURRENT = 0x03 SENSOR_TYPE_FAN = 0x04 SENSOR_TYPE_CHASSIS_INTRUSION = 0x05 SENSOR_TYPE_PLATFORM_SECURITY = 0x06 SENSOR_TYPE_PROCESSOR = 0x07 SENSOR_TYPE_POWER_SUPPLY = 0x08 SENSOR_TYPE_POWER_UNIT = 0x09 SENSOR_TYPE_COOLING_DEVICE = 0x0a SENSOR_TYPE_OTHER_UNITS_BASED_SENSOR = 0x0b SENSOR_TYPE_MEMORY = 0x0c SENSOR_TYPE_DRIVE_SLOT = 0x0d SENSOR_TYPE_POST_MEMORY_RESIZE = 0x0e SENSOR_TYPE_SYSTEM_FIRMWARE_PROGRESS = 0x0f SENSOR_TYPE_EVENT_LOGGING_DISABLED = 0x10 SENSOR_TYPE_WATCHDOG_1 = 0x11 SENSOR_TYPE_SYSTEM_EVENT = 0x12 SENSOR_TYPE_CRITICAL_INTERRUPT = 0x13 SENSOR_TYPE_BUTTON = 0x14 SENSOR_TYPE_MODULE_BOARD = 0x15 SENSOR_TYPE_MICROCONTROLLER_COPROCESSOR = 0x16 SENSOR_TYPE_ADD_IN_CARD = 0x17 SENSOR_TYPE_CHASSIS = 0x18 SENSOR_TYPE_CHIP_SET = 0x19 SENSOR_TYPE_OTHER_FRU = 0x1a SENSOR_TYPE_CABLE_INTERCONNECT = 0x1b SENSOR_TYPE_TERMINATOR = 0x1c SENSOR_TYPE_SYSTEM_BOOT_INITIATED = 0x1d SENSOR_TYPE_BOOT_ERROR = 0x1e SENSOR_TYPE_OS_BOOT = 0x1f SENSOR_TYPE_OS_CRITICAL_STOP = 0x20 SENSOR_TYPE_SLOT_CONNECTOR = 0x21 SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE = 0x22 SENSOR_TYPE_WATCHDOG_2 = 0x23 SENSOR_TYPE_PLATFORM_ALERT = 0x24 SENSOR_TYPE_ENTITY_PRESENT = 0x25 SENSOR_TYPE_MONITOR_ASIC_IC = 0x26 SENSOR_TYPE_LAN = 0x27 SENSOR_TYPE_MANGEMENT_SUBSYSTEM_HEALTH = 0x28 SENSOR_TYPE_BATTERY = 0x29 SENSOR_TYPE_SESSION_AUDIT = 0x2a SENSOR_TYPE_VERSION_CHANGE = 0x2b SENSOR_TYPE_FRU_STATE = 0x2c SENSOR_TYPE_FRU_HOT_SWAP = 0xf0 SENSOR_TYPE_IPMB_PHYSICAL_LINK = 0xf1 SENSOR_TYPE_MODULE_HOT_SWAP = 0xf2 SENSOR_TYPE_POWER_CHANNEL_NOTIFICATION = 0xf3 SENSOR_TYPE_TELCO_ALARM_INPUT = 0xf4 SENSOR_TYPE_OEM_KONTRON_FRU_INFORMATION_AGENT = 0xc5 SENSOR_TYPE_OEM_KONTRON_POST_VALUE = 0xc6 SENSOR_TYPE_OEM_KONTRON_FW_UPGRADE = 0xc7 SENSOR_TYPE_OEM_KONTRON_DIAGNOSTIC = 0xc9 SENSOR_TYPE_OEM_KONTRON_SYSTEM_FIRMWARE_UPGRADE = 0xca SENSOR_TYPE_OEM_KONTRON_POWER_DENIED = 0xcd SENSOR_TYPE_OEM_KONTRON_RESET = 0xcf class Sensor(object): def reserve_device_sdr_repository(self): rsp = self.send_message_with_name('ReserveDeviceSdrRepository') return rsp.reservation_id def _get_device_sdr_chunk(self, reservation_id, record_id, offset, length): req = create_request_by_name('GetDeviceSdr') req.reservation_id = reservation_id req.record_id = record_id req.offset = offset req.bytes_to_read = length rsp = get_sdr_chunk_helper(self.send_message, req, self.reserve_device_sdr_repository) return (rsp.next_record_id, rsp.record_data) def get_device_sdr(self, record_id, reservation_id=None): """Collect all data from the sensor device to get the SDR. `record_id` the Record ID. `reservation_id=None` can be set. if None the reservation ID will be determined. """ (next_id, record_data) = \ get_sdr_data_helper(self.reserve_device_sdr_repository, self._get_device_sdr_chunk, record_id, reservation_id) return sdr.SdrCommon.from_data(record_data, next_id) def device_sdr_entries(self): """A generator that returns the SDR list. Starting with ID=0x0000 and end when ID=0xffff is returned. """ reservation_id = self.reserve_device_sdr_repository() record_id = 0 while True: record = self.get_device_sdr(record_id, reservation_id) yield record if record.next_id == 0xffff: break record_id = record.next_id def get_device_sdr_list(self, reservation_id=None): """Return the complete SDR list.""" return list(self.device_sdr_entries()) def rearm_sensor_events(self, sensor_number): """Rearm sensor events for the given sensor number.""" self.send_message_with_name('RearmSensorEvents', sensor_number=sensor_number) def get_sensor_reading(self, sensor_number, lun=0): """Return the sensor reading at the assertion states. `sensor_number` Returns a tuple with `raw reading`and `assertion states`. """ rsp = self.send_message_with_name('GetSensorReading', sensor_number=sensor_number, lun=lun) reading = rsp.sensor_reading if rsp.config.initial_update_in_progress: reading = None states = None if rsp.states1 is not None: states = rsp.states1 if rsp.states2 is not None: states |= (rsp.states2 << 8) return (reading, states) def set_sensor_thresholds(self, sensor_number, lun=0, unr=None, ucr=None, unc=None, lnc=None, lcr=None, lnr=None): """Set the sensor thresholds that are not 'None'. `sensor_number` `unr` for upper non-recoverable `ucr` for upper critical `unc` for upper non-critical `lnc` for lower non-critical `lcr` for lower critical `lnr` for lower non-recoverable """ req = create_request_by_name('SetSensorThresholds') req.sensor_number = sensor_number req.lun = lun thresholds = dict(unr=unr, ucr=ucr, unc=unc, lnc=lnc, lcr=lcr, lnr=lnr) for key, value in thresholds.items(): if value is not None: setattr(req.set_mask, key, 1) setattr(req.threshold, key, value) rsp = self.send_message(req) check_completion_code(rsp.completion_code) def get_sensor_thresholds(self, sensor_number, lun=0): rsp = self.send_message_with_name('GetSensorThresholds', sensor_number=sensor_number, lun=lun) thresholds = {} threshold_list = ('unr', 'ucr', 'unc', 'lnc', 'lcr', 'lnr') for threshold in threshold_list: if hasattr(rsp.readable_mask, threshold): if getattr(rsp.readable_mask, threshold): thresholds[threshold] = getattr(rsp.threshold, threshold) return thresholds def send_platform_event(self, sensor_type, sensor_number, event_type, asserted=True, event_data=None): req = create_request_by_name('PlatformEvent') req.sensor_type = sensor_type req.sensor_number = sensor_number req.event_type.type = event_type req.event_type.dir = 0 if asserted else 1 req.event_data = [0] if event_data is None else event_data rsp = self.send_message(req) check_completion_code(rsp.completion_code) python-ipmi-0.5.4/pyipmi/session.py000066400000000000000000000072611436677633700173750ustar00rootroot00000000000000# Copyright (c) 2016 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA class Session(object): AUTH_TYPE_NONE = 0x00 AUTH_TYPE_MD2 = 0x01 AUTH_TYPE_MD5 = 0x02 AUTH_TYPE_PASSWORD = 0x04 AUTH_TYPE_OEM = 0x05 PRIV_LEVEL_USER = 2 PRIV_LEVEL_OPERATOR = 3 PRIV_LEVEL_ADMINISTRATOR = 4 PRIV_LEVEL_OEM = 5 session_id = None _interface = None _auth_type = AUTH_TYPE_NONE _auth_username = None _auth_password = None _rmcp_host = None _rmcp_port = None _serial_port = None _serial_baudrate = None def __init__(self): self.established = False self.sid = 0 self.sequence_number = 0 self.activated = False def _get_interface(self): try: return self._interface except AttributeError: raise RuntimeError('No interface has been set') def _set_interface(self, interface): self._interface = interface def increment_sequence_number(self): self.sequence_number += 1 if self.sequence_number > 0xffffffff: self.sequence_number = 1 def set_session_type_rmcp(self, host, port=623): self._rmcp_host = host self._rmcp_port = port @property def rmcp_host(self): return self._rmcp_host @property def rmcp_port(self): return self._rmcp_port def set_session_type_serial(self, port, baudrate): self._serial_port = port self._serial_baudrate = baudrate @property def serial_port(self): return self._serial_port @property def serial_baudrate(self): return self._serial_baudrate def _set_auth_type(self, auth_type): self._auth_type = auth_type def _get_auth_type(self): return self._auth_type def set_auth_type_user(self, username, password): self._auth_type = self.AUTH_TYPE_PASSWORD self._auth_username = username self._auth_password = password @property def auth_username(self): return self._auth_username @property def auth_password(self): return self._auth_password def establish(self): if hasattr(self.interface, 'establish_session'): self.interface.establish_session(self) def close(self): if hasattr(self.interface, 'close_session'): self.interface.close_session() def rmcp_ping(self): if hasattr(self.interface, 'rmcp_ping'): self.interface.rmcp_ping() def __str__(self): string = 'Session:\n' string += ' ID: 0x%08x\n' % self.sid string += ' Seq: 0x%08x\n' % self.sequence_number string += ' Host: %s:%s\n' % (self._rmcp_host, self._rmcp_port) string += ' Auth.: %s\n' % self.auth_type string += ' User: %s\n' % self._auth_username string += ' Password: %s\n' % self._auth_password string += '\n' return string interface = property(_get_interface, _set_interface) auth_type = property(_get_auth_type, _set_auth_type) python-ipmi-0.5.4/pyipmi/state.py000066400000000000000000000011471436677633700170270ustar00rootroot00000000000000class DefaultProperties(object): def __init__(self): if hasattr(self, '__properties__'): for prop in self.__properties__: setattr(self, prop[0], None) class ResponseDecoder(object): def __init__(self, rsp=None): if rsp: self._from_response(rsp) class State(DefaultProperties, ResponseDecoder): """This is a container that represents a state. The state can have default properties that are created by init. """ def __init__(self, rsp=None): DefaultProperties.__init__(self) ResponseDecoder.__init__(self, rsp) python-ipmi-0.5.4/pyipmi/utils.py000066400000000000000000000101271436677633700170450ustar00rootroot00000000000000# Copyright (c) 2014 Kontron Europe GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import sys import codecs from array import array from .msgs import constants from .errors import DecodingError, CompletionCodeError _PY3 = (sys.version_info >= (3,)) def py3enc_unic_bytes_fix(dat): # python 3 unicode fix if isinstance(dat, str) and _PY3: dat = dat.encode('raw_unicode_escape') return dat def py3dec_unic_bytes_fix(dat): # python 3 unicode fix if _PY3: return dat.decode('raw_unicode_escape') return dat def py3_array_frombytes(msg, data): if _PY3: return msg.frombytes(data) else: return msg.fromstring(data) def py3_array_tobytes(msg): if _PY3: return msg.tobytes() else: return msg.tostring() def check_completion_code(cc): if cc != constants.CC_OK: raise CompletionCodeError(cc) def chunks(data, count): for i in range(0, len(data), count): yield data[i:i+count] class ByteBuffer(object): def __init__(self, data=None): if data is not None: self.array = array('B', data) else: self.array = array('B') def push_unsigned_int(self, value, length): for i in range(length): self.array.append((value >> (8*i) & 0xff)) def pop_unsigned_int(self, length): value = 0 for i in range(length): try: value |= self.array.pop(0) << (8*i) except IndexError: raise DecodingError('Data too short for message') return value def push_string(self, value): if _PY3 and isinstance(value, str): # Encode Unicode to UTF-8 value = value.encode() py3_array_frombytes(self.array, value) def pop_string(self, length): string = self.array[0:length] del self.array[0:length] return py3_array_tobytes(string) # return py3dec_unic_bytes_fix(string.tostring()) def pop_slice(self, length): if len(self.array) < length: raise DecodingError('Data too short for message') c = ByteBuffer(self.array[0:length]) self.__delslice__(0, length) return c def tobytes(self): return self.array.tobytes() def tostring(self): return py3_array_tobytes(self.array) def extend(self, data): self.array.extend(data) def append_array(self, a): self.array.extend(a) def __getslice__(self, a, b): return self.array[a:b] def __delslice__(self, a, b): del self.array[a:b] def __len__(self): return len(self.array) def __getitem__(self, idx): return self.array[idx] BCD_MAP = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '-', '.'] def bcd_encode(input, errors='strict'): raise NotImplementedError() def bcd_decode(encoded_input): chars = list() try: for data in encoded_input: if not _PY3: data = ord(data) chars.append(BCD_MAP[data >> 4 & 0xf] + BCD_MAP[data & 0xf]) return (''.join(chars), len(encoded_input) * 2) except IndexError: raise ValueError() def bcd_search(name): # Python 3.9 normalizes 'bcd+' as 'bcd_' if name not in ('bcd+', 'bcd'): return None return codecs.CodecInfo(name='bcd+', encode=bcd_encode, decode=bcd_decode) def is_string(string): if _PY3: return isinstance(string, str) python-ipmi-0.5.4/setup.cfg000066400000000000000000000003071436677633700156440ustar00rootroot00000000000000[aliases] test=pytest [nosetests] with-coverage = 1 cover-xml = 1 cover-branches = 1 cover-package = pyipmi [pep257] ignore = D100,D101,D102,D103,D104,D105,D203,D204 [flake8] extend-ignore = E501 python-ipmi-0.5.4/setup.py000066400000000000000000000050331436677633700155360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from setuptools import setup, find_packages import os import subprocess name = 'python-ipmi' version_py = os.path.join(os.path.dirname(__file__), 'pyipmi', 'version.py') def git_pep440_version(): full = subprocess.check_output( ['git', 'describe', '--tags', '--always', '--dirty=.dirty'], stderr=subprocess.STDOUT).decode().strip() tag = subprocess.check_output( ['git', 'describe', '--tags', '--always', '--abbrev=0'], stderr=subprocess.STDOUT).decode().strip() tail = full[len(tag):] return tag + tail.replace('-', '.dev', 1).replace('-', '+', 1) try: version = git_pep440_version() with open(version_py, 'w') as f: f.write('# This file was autogenerated by setup.py\n') f.write('__version__ = \'%s\'\n' % (version,)) except (OSError, subprocess.CalledProcessError, IOError): try: with open(version_py, 'r') as f: d = dict() exec(f.read(), d) version = d['__version__'] except IOError: version = 'unknown' with open('README.rst') as f: readme = f.read() setup(name=name, version=version, description='Pure python IPMI library', long_description=readme, url='https://github.com/kontron/python-ipmi', download_url='https://github.com/kontron/python-ipmi/tarball/' + version, author='Michael Walle, Heiko Thiery', author_email='michael.walle@kontron.com, heiko.thiery@kontron.com', packages=find_packages(exclude=['tests*']), license='LGPLv2+', platforms=["any"], classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Console', 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Topic :: Software Development :: Libraries :: Python Modules', ], entry_points={ 'console_scripts': [ 'ipmitool.py = pyipmi.ipmitool:main', ] }, test_suite='tests', tests_require=[ 'pytest', 'mock', ], install_requires=[ 'future', ], ) python-ipmi-0.5.4/tests/000077500000000000000000000000001436677633700151655ustar00rootroot00000000000000python-ipmi-0.5.4/tests/__init__.py000066400000000000000000000000001436677633700172640ustar00rootroot00000000000000python-ipmi-0.5.4/tests/fru_bin/000077500000000000000000000000001436677633700166115ustar00rootroot00000000000000python-ipmi-0.5.4/tests/fru_bin/kontron_am4010.bin000066400000000000000000000100001436677633700217460ustar00rootroot00000000000000!(9|¤SbÇKontronÆAM4010Ê0023721003Å35943ÇEF_0100ÁÇKontronÆAM4010Ä0012Ù0000000000000000000000000Ù0000000000000000000000000Ù_________________________ÇEF_0100ÕMAC=00:80:82:74:09:78Á½ÀA÷Z1À‚5oZ1€àÿÿáÿÿâÿÿãÿÿ¤˜óQüQüqþqþ/þ/þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpython-ipmi-0.5.4/tests/fru_bin/kontron_am4904.bin000066400000000000000000000200001436677633700217630ustar00rootroot00000000000000!.:uÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ É‘ÇKontronËAM4904-SRIOÊ0400058419É1028-5232ÜFRU-S1100-FABRIC-SRIO-4P0-02ØMAC=00:A0:A5:5D:2A:9F/20Á$ ÇKontronËAM4904-SRIOÉ1028-5232Â04Ê0400058419Ê0000000000ÜFRU-S1100-FABRIC-SRIO-4P0-02Á ÀBöZ1ÀhÈZ1 àÿÿáÿÿâÿÿãÿÿäÿÿåÿÿæÿÿçÿÿèÿÿéÿÿêÿÿëÿÿQüQüQüQüQüQüQüQüQü Qü Qü QüÀà‰ÕZ1 àÿÿáÿÿâÿÿãÿÿäÿÿåÿÿæÿÿçÿÿèÿÿéÿÿêÿÿëÿÿa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaü a ü aü aü a ü aü aü a ü aü aüÀàˆÖZ1 àÿÿáÿÿâÿÿãÿÿäÿÿåÿÿæÿÿçÿÿèÿÿéÿÿêÿÿëÿÿa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaü a ü aü aü a ü aü aü a ü aü aüÀà‡×Z1 àÿÿáÿÿâÿÿãÿÿäÿÿåÿÿæÿÿçÿÿèÿÿéÿÿêÿÿëÿÿa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaü a ü aü aü a ü aü aü a ü aü aüÀà†ØZ1 àÿÿáÿÿâÿÿãÿÿäÿÿåÿÿæÿÿçÿÿèÿÿéÿÿêÿÿëÿÿa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaüa üaüaü a ü aü aü a ü aü aü a ü aü aüÀOúõZ1- ÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÀOùöZ1- ÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÀ‚ë|WZ1- àáõáõáõàáõáõáõàáõáõáõàáõáõáõàáõáõáõàáõáõáõàáõáõáõàáõáõáõàáõáõáõ àáõáõáõ àáõáõáõ àáõáõáõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpython-ipmi-0.5.4/tests/fru_bin/vadatech_utc017.bin000066400000000000000000000020001436677633700221550ustar00rootroot00000000000000!ÌÈVadaTechÇUTCA PMÇ4451956ÆUTC017ËMgtCtrl.binÁ*ÈVadaTechÇUTCA PMÆUTC017Å05.00Ç4451956ÀÀÄ5D32Á?À‚©Z1'¤python-ipmi-0.5.4/tests/hpm_bin/000077500000000000000000000000001436677633700166015ustar00rootroot00000000000000python-ipmi-0.5.4/tests/hpm_bin/firmware.hpm000066400000000000000000003135641436677633700211370ustar00rootroot00000000000000PICMGFWU˜:¥˪[ øýüPIPM Controller—@–‹@-ˆlÛ¶mÛ¶mÛ†·¬…c[˜µà²…cZÐÛ¶mÛ¶íÛö}ÛWmß±öµjß­í{k_Òí»Mûl_öö´¯~û>Ó¾µo’mÛ¾º¶mÛ¶mÛ¶mÛ¶mÛ¶mÛö.º½SÙÞ½ÚûfíÝ’ö.G{ߨÞ?ÕÞÖÞ jï·m{ÇÔÞ³í}x{OÖÞmï³öî²u_­[hÝ·Ö­hÛÖmغÅ[÷–mÛºkÝ­[iÛ¶îmÚ¶ulÛÖ½½uOZ·~ë~¦u?Öº“lݺÖMkÝÓ­ûáÖ½jÝŽmÛ¶­{SmÛ¶mÛºù¶mÝä¶mÝÔÛ¶uc[÷Ñ[÷ñÖŒ‡Òsõzî=-&ñä=ÇQ/ê¥ðŽmšÈúku&K“Äÿôù¼2Ií®£äý%G5›35lé\ƒ›˜ÓÜÃÜÃÜÃÜÃÜÃÜÃÜÃÜ苬G0u!ý0÷0÷0÷0÷0ê"«Á‘ã .¤æ@]d58òlPÒsss .²$¨ 釹‡¹‡¹‡¹‡¹‡¹‡¹¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mû§¸«Xò?š*UÝÆcÕózȵ/t¹´¥žùžbZçEÄó’Žã= º…§¯œZ/‰z/PÔm\Å’ÿÑT©ê6«ž²Ô¾ÐåÒ–zæ{¢ÃµÎ‹ˆç%Ç{t O߯´^!õ^ :¨Û¸Š%ÿ£©RÕmƒºH8ͺëE2¶¤lR˜ßën5 |9ïWÁ ö —bˆtr ®Wëä§ b•7P0ˆ°?ÕèJÔm8U÷¿ß$¸û ÿŠ_â¨.Ûõ ‘N¼ú}ù)ƒXå  "ìO ü^÷Çf /r)ûƒàz¹Nž¤ç¼¯çõ )”ÁUˆ«¹áÐ+Ý)þ=T Ö‹Ï .Ns .’¡"x$zÊc1¿ÜžòX4EÀ#ÑSLÄíßÿ¡ ͤé‚q<ÝwÅK×^qÅD7_q0M÷¾â`CtÝ+óe:5Hë¼Ò0ìC¤^Aì–ÿó4.7Ø;Xºí¿ÁïLýÅGï>X™¯c|ÔHûñQJ í–±îu†È.7v°} Ø þæV滎Š{ýú®çæVÜkݱêcZ畆aÅS¯ ö(ÿó4~{ ¤ÛþüÎÔ_|ô ˜â£î@Zñø(%ö(±îu†È~ÒØ‚½àone‚ßc H{I×ss+6ïXõ1­óJð‡Ô+ˆµúŸ§i Ôr°wkºí¿ÁïLýÅGï>X™ ">ê¤=ÄG)´V±îu†ÈZ¤Ýº{ÁßÜÊýèX°×Âw=7·â`âXõ1­óJð~¥^AìÇÿJ ¤UcÝ+>ê ‘¤Û‚½àon’ ö¢ßõÜœ1§¯u^i–-õ boÿó4ƒ½Štۃߙú‹Þ}0™ÄGÝ´lñQJ í-Ö½â£ÎYb ­bö‚¿¹‰X tk°÷ð]ÏÍÅ’.|¯û³Ì@_ÖÔzI)áë¬ó×uAhšu‘Ìsï)OÖ“÷”gwýƒî{ÕMczÔ‚A›Òú#ßõ…ËGö½a÷ûÊèžV&¿eÏ«üGÁ vÃM&†üºçüGÏëå§ b•7P0ˆ°?Õ8ؾšˆó=å™:fÀûzÊ3¿ñëžð=¯7½þøòì=ЪR¿WŸÎˆ(Ù:¨ BÓÜÁë{Õ s4&¹üGO¢ÃÓܾŽBzźo|ÔhÎÞSb@Ýá,MÂ4‡>¹ºãA))3æšÇƒÞÓÄýy/¤f<¯x¯_èÓú<æêŽ¥¤Ì˜kzO÷çq¼šÓœ§=š#8¬. †.ŽõÜ{*4”ú1¶ üØ•iÓñ@O…†&j@€wÿ&>X™ö³o¢ùž Ó¨ú`ÅõI¾weÚÏ8Öf.a1ü h¢ã#)?veÂê¶=̾±×N¯0›ÇÞûu¸Âì{íô ³y콇w}Ãì{íô ³yì½g®Ãµv˜}c¯^a6½÷pm®UáØ7dµSb 6Yïq¼÷0ûÆ^;½Âl{ï™ëp­;̾±×N¯0›ÇÞ{¸–fݱ—’^aöŠ½Î©—c-lÊå}1ü h¢ã#)?veÖ»ÛvyåvIS^ÿµ²lÆr=ž‡Ù7öÚéfóØ{¿±×B˜}c¯^a6½÷ð®o˜}c¯^a6½÷p­fߨk§W˜Ícï=\›‡Yw쥤W˜½b¯sê}äXË{˜}c¯^a6½÷LÑáZ³îØKI¯0{Å^gæ»»:Öò{a¢pù.†á}ÑD ÆGR~¬ñAl¬{Á÷®8ÐbU{eö˜0ûÆ^;½Âl{ï1*ÌÞØXû›7ÿ‘JPo¾×8˜¯FÂ>Ûˆûî+®ûrÇZa<{µR.Å¿Õú•ã†ÕJv;ÖŠ^´–•ø^}0cÓ@ÉÖg¹VBÊ9ÖŠÞ×JÖ+âxLÞí"±ZÉz%8‘Ô¼#Gâø”ÔäÑþ'OHÿ¤Œãg •ìNñ/hýÊáֲ߫0cÓ@ÉÖñmÂì{íô ³yì½g· ³oìµÓ+Ìæ±÷ž):\ 3ˆµcUÛ÷N”†Ù7öÚéfóØ{¿±×B˜uÇ^Jz…Ù+ö:§¾û£_Î8aLnaÖ{)éf¯ØëÌ|wWÜP/àŠ0ûÆ^;½Âl{ï™T†ká†é·,̾±×N¯0›ÇÞ{æáZ³îØKI¯0{Å^gæ»»z §Bûhàðú]|^šæf¯VBÊ}úms­_9nXÇ’ÝÓêÝ÷Ý¢á_Ç’³'†á_Ç’;†®d2þÕÂaé?¿2» Ý:v«~èwLbO¬‡~Ç$Çøf«Ð¯…ƒîï„÷­¾)ú¸xèwLbûì ÂÐï˜äßÜDúµpÐý,óñòÙÆÜ—Oº\ièwLrŒOää#ôká ût°ïºÂ²Û#—ÿ(´ÉAwàê¥Þýò}°2m‚¡kšx ‰ÈÉzi}È¡~ùŽ%b|ÃÄôT ñ½F…Ï6ò¾|ÅÁ˜o½âàXß”•ù.éXbQ9nãó.’}BA5pIù±+Š&j%n(û†¬vJ Äæ!ë=ŽÂì{íô ³y콇³°â3ÇÞxTŽÛ¸ÇXX$û‚Û&jà0>’òcW&|Fµ7ˆ}CV;%bóõÇ aö½vz…Ù<öÞÃYX™ *˜³yè"{Ãj0¬KôÑbHß@^;},†4ä½’¡s$7% ³oìµÓ+Ìæ±÷~c®…0뎽”ô ³WìufZpìÍ*ÇmÜcÞE²/h¢ã#)?veöš¨•¸¡@ì²Ú)1›‡¬÷8^³oìµÓ+Ìæ±÷ÎÂdˆ‚9›‡.²7¬ú5†ô äµÓÇbHó@Þû³Q2t ’›Œ’«Áv„aö½vz…Ù<öÞo¬Ãµfݱ—’^aöнÎÌwwÅÂì{íô ³yì½g>!\ aÖ{)éf¯ØëÌ|w×Û¸¿8–ˆñ1ï"ÙlõÒÀa|$åÇ®ÌZº¾±ª^¡k«ÞÃ]]ßXÕN¯Ð5Uõº¾±ª^¡k«ÞÃYp,Qþ1ö"ÙÞõÒÀa|$åÇ®ÌÊ]ßXÕN¯Ð5Uï#nwßXﮡë«Úéºæ±ê=St¸B×7VµÓ+tÍcÕ{8 “ÁBWw¬JI¯ÐõŠU™6¾eèúƪvz…®y¬zÏÜ"\ ¡«;V¥¤WèzŪÎÌwwõ@Oý_¢Ãë_]|^šæP7=nóø2Å’¿a¬ûÆGíôŠuóøè]˜‹«@ÆùuOy†^Æ›µ¢c]w|”’^±îuf¾»«ƒÜ·ç¿FŒ~öiº8Öc :ŽRYÏX±$¿]½±Žäú*¸t ŸxºÎÐ…Ù{ìu§W˜¥ÄÞ e®8,ö:W$@Ú±lÓ±÷ÝeŠË¡|Þ‰Ï:X;dÍ¿ÎÚ´#‚¨q+tX;dÍ¿ÎÚ´#‚¨7R1Ö—…†oˆµCÖüë¬M;"ˆºßº@¬²æ_gmÚAÔ©ëËBÿqÁ ø¹±¢Fbí4³ö÷ªMc "ÃÙ÷]˜µcoþ½jÓ˜‚Èý I!ÌÞc¯;½Â,%ö^l\$–’^)3{IÝ æÌ)$>-g‰µCÒ<ö¾XI_ßEUP0gN!ñi3¸0{½îô ³”Ø{¡LãXì}}×v!ö:Yö®÷IÐÅçiî†å}=HjE 𾞊 ¾skE¸Ù x_Oõon­7»ay_O…ÌÓž oŽà°º0º8Öãî©ð†npŒ­?ve^˜<ÐSá õÒ€ïþM|°2¯OÝDó=»o0ã6¾‡XqµÆ7°âšõ ³â^Á§tŽÞ|rZq6Ÿ+.7}Û¬¸|ˆ¶2¯!Ž]cæò] ÿÂû¢^8Œ¤üØ•y‘Ò{òÓº¾±ª^¡k«Þã*𾞠ﶡ7t}cU;½B×w¬?½.íÅð/ ^8Œ¤üØ•Iqº¾±ª^¡k«Þo¬Ãµº¾±ª^¡k«Þy=Þ9Öú¦\¾‹á_x_ÔK‡ñ‘”»2)"C×7VµÓ+tÍcÕ{8 Ï\<{O’\èúƪvz…®y¬zgE¶ýâÙ{*Ì0C×7VµÓ+tÍcÕ{8 ,Éo¿xöž $3t¡ë«Úéºæ±ê=œV·_<{O’¡ë«Úéºæ±ê=œâvõÛ‹¢áf<{O’jèúƪvz…®y¬zgŹýƳ÷T ™ÑoÅÜZnöíÁ­áfüGO’’ÑÄ= JB×7VµÓ+tÍcÕûµc&pi/†õÒÀa|$åÇ®L÷óÐõUíô ]óXõ~c®…ÐõUíô ]óXõÎè©@2\û†®îX•’^¡ë«:3mÇ"ÜË|1ü ¶zià0>’òcW¦{° õ ÇÚ©xК‡c晴aÁ(` F7ÆèÊì1A¸/){…w8Æ0QoX –çCèËòÌ ï“B×7VµÓ+tÍcÕ{ä…Û“1†É»aø‘¼YÚS±*"t}cU;½B×ú^,õVþ£§¹VyÇFRLž¤§Bš÷õTd¤Lž¤§‚Áûz*8Ü[èúƪvz…®y¬zÏœ ×BèêŽU)éº^±ª3óÝ]q‚ÿv£ÒÐõUíô ]óXõÞBWw¬JI¯ÐõŠU™ïî:³CÒü=YÇÞ»Ï6Þboî³jì½ûÞÉ{sß;Éoãâ¯òð ,t}cU;½B×®ò=HJF÷ 4@(™æX.ž½§ÉÇøž $%£‰{ ”Ls,ÏÞSäãå?z*”Œ&îh€P2Í-ŽŒÞ–^Þ×SáÝ 4Xž«/Ëų÷Tx÷¸n,ÏÇ՗寳÷Tx÷¸9 –çãêËòÁ³÷Tx÷8dz÷TxÇ ÐCï·E+ÂÍŸ§9PÉP$]e1ü 8Œ¤üØ• ìž:EÓ*xC7˜zê|2ÁƒVÁºÑáVƒåùú²êF•ô}ëÖ`¨O ¢ÆñºÃì{íô ³yì½²è±*Ìë ×ï«Â÷­[ƒ¡> aö½vz…Ù<öÞÑ "ù¾±´öE<8]!\›ßÆÅ×aö½vz…Ù<öÞG~ÏãêýòNHµê1ð»à°Ô°bÉ!÷´ê1pÈbßÕN‰ØŸÕd L õž¡«;V¥¤WèzŪÎÌww‰Þ‘þ˜&ñXíÃô¼% £¡Þ3tuǪ”ô ]¯XÕ™ùî®3Ñ;ÒÓDøw£ÛÆWïùK`½ø¼È˜æøuOÅ—ÿè©ð¤¿î©xHþ£§ÂÅý:èF^Ó¿î©`ð=\zñëž iþ£§"# y8{O‰u‡³”i.̺c/%½Âì{™ïîfߨk§W˜Ícï}šóä=XtN‡Õ6ÔÖ`y>„¾,U±Ð¯ÍÝâAíÙg'ïë©8jè׿nñ ö Ëûz*ä˜æøžŠ£Ns ka½HöżÛ=yOyfž{O¥¾a[~ìÊœ§ãž ,š¨Þýú`eÎ?ûödþ£§B.£êƒLß»2çŸq,AÜå»þÛF5pIù±+s^³øñ=Gå?z*äˆYžYÐ*¯È˜uØ0ûÆ^;½Âl{ïqx_OÅQ±oÈj§Ä@l²ÞƒØ—§ÛS!‡ï4nOxM„ktÿÇ}¶¥ß›üGOÅQƒë°†×„ô2®ØX›ÿè©c"«ÚÜ-㊵qá{ÙSfª¾œÿè©8ªï4Z×àøæ±oÈj§Ä@l²ÞãxÁ±TI…Ù7öÚéfóØ{¿±v,Udø=G ræ}=r„Ù7öÚéfóØ{ÏíXªÈ¸Ìÿ€&jà0>’òcW&* ³oìµÓ+Ìæ±÷~c®…0ûÆ^;½Âl{ï¼žŠ£†kß0ûÆ^;½Âl{> 9µv˜uÇ^Jz…Ù+ö:3sÇRôÌŽY‡½°2í¹Ù»È~ÁéF5pIù‘\™¶EnO¾acÖa~¹b¯à›ß®Øðfߨk§W˜Ícïý¥c1}÷Y0º}V+³‡Yw쥤W˜½b¯s¿bV‡}×°Ûlø ľ!«±yÈzÏ.QÊv3̾±×N¯0›ÇÞ{¸Öë‡Yw쥤W˜½b¯s¿|NãÀ†Ï0ûÆ^;½Âl{ï™K®…0뎽”ô ³Wìuf¾»ëÿäÉjß}Œ¹ÏjrŪ¶&[÷Ý0ª æì¼„çûå{WæEò#=x}¯ÎÃéQ{*ž‰ÍCÒ{ʌľ!©ý#=x²Š#™¨°­ð(üïÉÕ™ ÿëŽäJQɤ*l+<Ú‰{r±ª~H ?R£êƒÏö½+“B¥c—_ß°1«Ã3ˆY‡A#[wÌJI™‘m³ÞgBÌzyÚS±1²ŬÊŒl1«äŠIìjÌ*ÆôTÈqð ®b‰ÇHl+fd+ÆôTõ ‹YÓØ “P0gè"fߨk§W˜ÍcïÊÈV|”…2¤+>’F•C¡$$Gg”4̺c/%½Âì{»ÓõÜ]5—>OÒS1NÞ×SaIÊäIz*¶å}=›áÛh /}~|OŶ¼xOÅf©8?¾§bœ¼xO…% ϺíÓaÿÙ’ bV‡ƒÜaŒ*:utäÛZÐÀazcÖa‚\‡«6f&¸æAî=½‚«;È¥ô gí”Ð<œ½Ï>Ç Á5rïé\ÝA.% îp–’z…³ÎÙ»r‡qìòÿ ³{ae¾ÏhEbûü;O Œîï[À_âøpÙaˆOÞ÷˜Õ/Ÿãå³2æD,ž¯BÊŒYOý"e5±ºa5Xž¡/Ë3ÛÝß·€¿Äñá²~¹ó蘒`Î(ÐE6̾±×N¯0›ÇÞ;”‘í@ºùÃpÉ( eHwÀ3ßV€2¦6GA\s ’›Œ’¹µØûtØ7dµSb 6Yïq¼WìIìƒ.û†¬vJ Äæ!ë=Ž×äúe|8̾±×N¯0›ÇÞ{8ëäü ×:qö0ûÆ^;½Âl{ïPF¶†á,„k¯@ì²Ú)1›‡¬÷©xvaö½vz…Ù<öÞãªà³'·0ûÆ^;½Âl{ï™y¸V³oìµÓ+Ìæ±÷ž¹}w ³oìµÓ+Ìæ±÷žù†kí0뎽”ô ³WìufÎï®Z‘ ù`bî»ŒŽž^þ©­‚7¨lΈ+˜3ºÈ†Ù7öÚéfóØ{qè#¨ ·y½§âñÖB)(Ɉ©Ar“QRœô·h°ÍÜ*Ã[¿×•‘U¡²åg­—DÁ„Êæf/ïë©ÐÓ™°Køž =õ½„±|¯ñvƤ€&j°ÉGÌŠ,Þ×S¡§¸Ø ‹»ÑD 6ùˆY‘ä"ke©Ø ¾wÅI˜}c¯^a6½÷¸ >æDø^×Qgª¾*ÃWè¹ÃLð0ûÆ^;½Âl{ïá,L>Âì{íô ³y콇³odv˜pÖŽÌ:¬ÁÐÏàb³3fu8fFäAÙ*ÕŠÍ+³TBex@ópöžêg)Aî0q¼/T†4gï)1 îp–ä:ÇkCex@ópöžêg)A®œâxïAî6PÙò1Ö<½§â1Vw(J _Ý ®J¡²åc¬y(zOÅc¬îP”¾R‚\¥PÙò1Ö<½§â1Vw(J _¯ï園U¡²åg—ÖKò­lóW¿—Ó@V…Ê–ŸQ´^*› ³oìµÓ+Ìæ±÷žùî®aÖ{)éf¯ØëÜ]q‡ƒ\dùlã{Á÷N¬Ce¸ñö½œŽY*[~FÑzIL¨l r‘å{'îÄ¬È r‘õ‹ò°íaª"Ë_ØLPn÷9vÙÜÁëUÉü…m…G7ì~\qʘZv­H(~ìŠýï%8`¦ú­»ZŸeed˾â``fA«¼"cÖa  é²ÇXóPôžŠÇXÝ¡(e$Ê^z²c;É/\r¹ÊàJOÈUO‡ ¹úÞH…\µ‡¿!¢¹‚9£@Ù0ûÆ^;½Âl{ï£,úÊàºlô|ÄiÜd”ÊàÊÐ<œ½§Ä€ºÃYÊ,ãõ+\ÖáÞaÌ w(Cºôvbëðuè~Q¶=L…2¦Öþ+*(·‹6ó¶^e+—‚9û/áù~ù쉋?Òƒ×! ed;^q$¶)ì èbø/Êö_gçaŽvÄ嘰™0HÐï|íT<è7¾w”Dƒä&£¤ æ ¼  鎞ýºƒ/%ú½‚¯s¿vŸµ `Î~ÁKx¾;#³ÃO´sÿz¬¸sòë±2;¨Û“K•«ÚÝ/Êö‡©PÆÔ¢Ã¿cEåvñÃfÞÖ«l± æì¼„ç»sââôàuÈBÙ*Žd¢Â¶Â#E‚]] ÿEyØöëì<ÌÑŽ¸6 ú}ƒ¯ŠýæÁ÷Ž’hÜd”TÁœ¡‚”!]CáyЯ;øRRñ ß+ø:÷k÷Y æì¼„ç»32;,ðDû1±_‰àØí½acÖaC‰û',̾±×N¯0›ÇÞ{æáZ³îØKI¯0{Å^gæ»»z §û#ÕÀáõô{èvX/>/Ns7,ïë©Þ×S1›æ@] ‡õ"Ùózî=³›3gÌšÞ°1«Øl>f=xÚS1Td;³Ž¥ÌÈYUO{"ldk9fÝš2#[ 1«Õ ³ƒëíksF¶·˜•äÞ$vÌz»acV¶àªøšÇœ‘­"fe r;fUì0ætÞ‰‚LÜ3ˆYÅÐìWmªU—ûFÇêW1ÔMôìÛö™“÷õDX~ §BXº]ដOyÚS1”V}Ò?Ö(ž þÇp„ž‚²­k­‚7”ºÑ} üÈÆUO!c«¡VÁJÝø¿èW½ÿö•YùèP+®Ø0›ÇÞ{z…Ywì¥(ŽdþÐÅTôÑèš=× ¹É(éNöÁdÍz §âSŠ. ,eIôT|ÊÓžŠ¡´ê“þ±ÃïÇg[5ð帊f=ÐS1”"Ë_KYò²Q@ý÷ÙŠ¿Pÿ=…'neÓzIf/ïë©êÖÈVÌ=CÅÔÎGfÇÒ#1µhdV½±ŽÌCº·/â1gLí-2K ®·D;²½¡Ù¯ÚT«.÷¾×hõ˧Æñ?&£ð½`g[5ðå»FŒbt^£ÀôTˆK·¿ÖÂÏþ=ÔÖ‹Ï .Ns ka½HÆ–Ôsï©”:ŠÙíÉñLG¤{ìG‡CÃC¿#üB³ÐEö†Õ€õñQÒv—Ì,h•WdH ³<$}C¿£4ß¹ Ò+¸¾A®WÿÔÞ¹ Ò+¸¾A®WÓôîØgï«ùKb[ÀJ˜aönÅŒÄÞûŒÊôTˆÂü%±^‡¬: 8’I¶íjÜ ¼ž Q˜÷oÞù½ §C¹zÃgÞ˜DlÚÉ;Z›b'ïH÷¸âjPè|`óÕèÍ÷®ÌŠv¬¤öÕ¼þüõ@’ºïúµCRʾš×Ÿ¿ºùçß«wâ•2Ü.ßBJ é¾ñ­cµŠZ¥â1VË¡èÖ‘r‚ˆ]< êåØgÔ}5¯?Þ<1kŌĺC¿¹$kœ„~ï!)%ôëÆé‰½wgÌ}Ò“ñûj^þz@’ºC¿vHJ ¼oè׎Ä^8û¾š×Ÿ¿BR÷ì I)!é…ÿ^ïƒÙV |9¾u_ÍëÏ_Xø]÷Õ÷í]ƒ¡>±oÜW’™¼Ìý?yÅéEæò䶯æ/‰…ßu_}ßû«ÁPŸPöûjþ’ؾ÷1÷ÿä§—þÿdã²§:®ûJ2ó‘·í«yýù ¿ë¾ú¾½ãÖ÷Õü%±}ïcŠ}%¡ìç/‰…ßu_}ßû«ÁPŸÀßvßø#%…õâó"á4]X«•þp­ŒgˆåIzò¼¯§¡ŸÙËûz.nXÞ×Óß'<í©p©Ãê ËûzÊx_OÃ÷õ4D]$œæÿ˜wÌsïiˆM†nqÃje$C·ðÌ·uÎ@+#Yè×ö½Æ+ôûûÞI~Ãâï öÄîÉOÞ‘î{èØ"cšƒ. þbÞ14ºÅ «•‘ Õ³Çr['ºí ´2’…~mß;™Äª¶Ïž¨³<6Öýº}0Yãb±ª=Y‡~Ý>˜¨<{OC?œzÃÆƒºñ‹gïiè‡ëÌŽµñÇbUÛ÷NýÚ>{¢ÎòxP;Vuû`²ÆÅB¿öd«º}0Qyöž†~8õ†uãÏÞÓÐ×™k£[ܰZ¥MtÛh•6që¡_{r‹UíÉØ÷zõ<ûÐÀ—«ÁP›àzgÀûz"ÿ:]šæ  ka½Höż¼b ž{OÃê^[~ìÊ1=ÐSáÓ€ï~ù>X™#ä&nž¤§BùÐ/øÞÉ6÷ ýÚéxóÐï=“Êp-^wè—’^÷ ý:3ßÝõ†É±~Kå o §Byþ£§âã¦|öм|÷'<Ô+NXÒ÷®8aÐ>ŠñßµâÞŸ°âÞm|£wwØ÷¯¸;îs®8ŠT_ºâ.šOlÅ Ÿ}èŠÈ>uÅ se "ë·Aç´nŠ^ÇúíqtPÿq£àƒ• rl’]þ‹á_x_4Q‡ñ‘”»2AD˜}c¯^a6½÷ø èÔï|Wÿ­Œdaö½vz…Ù<öÞo¬Ãµfߨk§W˜Ícï=¾µÔpí«`ζ„.²hö«v€8¯ArÜ(ik©Æúï–ÿ­ E¹ˆ}CV;%bóõÇk×Á±oÈj§Ä@l²Þãyó w¹Ø7dµSb 6Yïq¼÷0ûÆ^;½Âl{ïñ-æáZw˜}c¯^a6½÷ø–=\K ³oìµÓ+Ìæ±÷ß²®½Âì{íô ³yì½Ç·ÔµÎ0ûÆ^;½Âl{ïñíÃáZ!̾±×N¯0›ÇÞ{|ë·áÚQ˜uÇ^Jz…Ù+ö:3ÿÝõ†ýEàÖ‡Û¸b¸fߨk§W˜Ícï=“Êp-ü>{¢³îØKI¯0{Å^gæ»»Þ°¿ˆŸ´cýV tN+‚cë·ÇÑAýÇ‚Væ;s,íwi/†âF5pIù±+óC“}­êfߨk§W˜Ícï=®Âþ ̾±×N¯0›ÇÞ{\}ãA!̾±×N¯0›ÇÞ{\µãAß0ûÆ^;½Âl{ïq5µÃì{íô ³yì½ÇÕ{ÊʬAaö½vz…Ù<öÞãª;áµâJ¥Vú**’'éÉ&Æûz²¡)“'éÉöó¾žl)*’'éÉ&ð¾žl£”É“ôd»x_O6gà}C¿pàx’žlïëÉ6J™fߨk§W˜ÍcïýÆ:\ aö½vz…Ù<öÞ÷¥ñ¨o˜uÇ^Jz…Ù+ö:§Úû£ß"8Ácì0ûÆ^;½Âl{ï™GÅÇØ¸â;̾±×N¯0›ÇÞ{&•áZ°Æ^™wcýv:§KÇúíqtPüGO6{e¾ÂÜ(ø`e¾ ÇN³»|ÿp¿£‰8Œ¤üØ• E“}­š|˜}c¯^a6½÷¸ ûƒ0ûÆ^;½Âl{ïqõ…0ûÆ^;½Âl{ïqÕŽ}ùž ½ñ 6ÿÑSqƃæüGO¶ÆƒÞùžlêæ?z²½ñ ”}å{WF§~ç»\­ Eï«É…N-îF§Ž–Þ°ZŠ®z¹Û„t)ñíåO]™µ$È% ¸¢†×Êì ‚\‡ƒ«jx­Ì" r‘\ç ¯•YRÁu¹Aî`é\"Èà§ôô÷)Þ@OKœù)=ý7ÐÓ߸žO†×a‚\¥ü”žþžÄèéOê„à b#H ³oìµÓ+Ìæ±÷~c®…0뎽”ô ³Wìuf¾»+¾=̾±×N¯0›ÇÞ{&³oìµÓ+Ìæ±÷žù· ³oìµÓ+Ìæ±÷žiI¸n¢ñ$±Ãì{íô ³yì½gWzŒ+=ƳoìµÓ+Ìæ±÷žI%¾õ;̾±×N¯0›ÇÞ{æ\¸¬±W¦ý;Öo‡ ƒú¬ÌRéXjÛ¸|ÿð¾h¢ã#)?veöèh²¯U-nqÃje$C·ðÌ·uÎ@+#Yè×öÁÄ}Ãîñ±x¬¿50Ég€¿³wßxÃÆF ´¯|¯‘=̾±×N¯0›ÇÞûu¸¬;öRÒ+Ì^±×9õÝÍr ¬Ì›hœà1v˜}c¯^a6½÷Ì£âŠcl\qŒfߨk§W˜Ícï=“Êp-Xc¯Ì÷ñ~;ÔÜ(ø`eÂ1Ž}ø¡.‰‹á_p»ÑD ÆGR~ìÊ„ šìkÕM…Ù7öÚéfóØ{«°?³oìµÓ+Ìæ±÷WßxP³oìµÓ+Ìæ±÷WíxÐ7̾±×N¯0›ÇÞ{\ÍãAí0ûÆ^;½Âl{ïqõš+˜¡ß7½nXÙ³…2²xá†=pP†t‡@v@S;DÁÜõ!; Œ©fߨk§W˜Ícï}ÄqÐE6ô›téeHwýÚZº% ³oìµÓ+Ìæ±÷~c®…0ûÆ^;½Âl{ï±|óxÔ7k¾‰fݱ—’^aöнÎ}“ß"Žõ[óÛ±~k†Ù7öÚéfóØ{1G\d«'1«™2#[1«vfߨk§W˜Ícï=¾µÔp-¹–:!fߨk§W˜Ícï=¾ÝT¸ö r7…µ±î•’±WÈêÜã[ì;öRÒ+Ì^±×™ùî®ÖØ“[˜uÇ^Jz…Ù+ö:3ßÝõ&úÔøcèœVm‹¡sZõÄ»Ñ9­z"üòq¥zm|°Àû†~íô ¼yè÷ž¹E¸¯;ôKI¯À{…~™ïîêž —¿D‡×¿ä{(ëÅgè"á4Çô4”MsÐ…Á‹c¿˜Wð¸{.b/¿â@âvÇ®ž’¡zæ×=ý}Šÿèéo‰Ó‹_÷ôw4þ£§¿q/Ϻ½cl|†_÷ô÷)þ£§¿%N/~ÝÓßÑøžþƼ<ëöî@#i²Žÿèi˜ð ã§ôô÷$Þ@O¢è~žþžÄÓíéOñŸ_÷ô÷$þ£§?QMÇ^þ ó—¯¼N© &ÿÑÓPOù)=ý}Š7ÐÓß§D~JOGã ôô7®ðˆ¤§¿£ñt{ú7=ÂÒÓß§xº=ý-ñAÇOééo#o §?8á;ºŒFlìå¡‹l ‚ŸòƒôÌ(X™€_÷4dð= ¹ôâ×= ¥ùž†(s²ºïZÉnؘÕF§î»ViSÁœù¡‹l˜}c¯^a6½wôÑ÷‚î»V©’ÆRWf-¢`fºÈ~¡ç·õ#$j7JJ,OÒÓßFÞ×ÓŸ b/oÜFÁŒ½’òcWfN-½aµ2’i¥ÏO¥Ä£¾aö½vz…Ù<öÞõv˜}c¯^a6½÷pmfߨk§W˜Ícï=¤ûÆ£ÞÃì{íô ³y콇tíxTw˜}c¯^a6½÷©o<*%̾±×N¯0›ÇÞûTˆG½Â¬;öRÒ+Ì^±×9Õ¹?ú·®¼+”·Ãì{íô ³yì½gR®ïíÉ-̺c/%½Âì{™ïîz÷ßø=T€õâó47³§9èÂZX/’¡ðzî·V=Ãs­ˆrÀzñºH8ÍÁz‘ì‹yÇ<÷ž† ”:o §By£@VfI«-éò] ÿÂûjà0>’òc'ÈÑdè7¬VF2t Ï|[ç ´2’…~m_>¹nØ}ã p«x¬¿5ùÄ=ƒ}#îž½ûÆ66¡}å{bZé}–k`eÞDã±Ãì{íô ³yì½gWcãŠcì0ûÆ^;½Âl{ï™T†kÁ{r ³îØKI¯0{Å^gæ»»ÞDÿ¾‡ŽÁzñyš›ÙÓÜÌžæ  ƒ¿˜W@©+Žd®GUÞ­ØÝ4ÿÑS¡¼QðÁÊ|]t,¢.ßÅð/ºÑD ÆGR~ìÊ|C§Þ°q¼Î0ûÆ^;½Âl{ïqöãaö½vz…Ù<öÞãêÇ aö½vz…Ù<öÞãªÇû†Ù7öÚéfóØ{«y¯fߨk§W˜Ícï}ô|ÄE¶î˜•’2#Û “ÏZ½‘ü›hõF¢ÕÉÿ@‚Á—O.O{µ8Bß}bÏÒ<’Ø{º„x–¾‘ÄÚ¸u‹1Xãžlãi×m$6Iï)3û†¤v(¹WX´Îô¨¡äºÃ¢¥Ü°!©0ü½aw ¾•Ƥ vÉ÷z“œIkàËVN7Í þ¬:Lò™g95÷àÏÊ馹ű¿|üúVÉÕ`¨M>ŽöL2z–xð£epøõ­’«ÁP›(q4sûðÁ–a.>Os7,5Ñëqÿkš‹giIì=]Bx²*'Ì…³v ô öÚé̓½÷pí»“^‡¬Ê s}ÃY;ú{íôŠæÁÞ{<ª}ƒ½vzÅ@ó`ïýÆ:\›Ç@ß`¯^1Ð<Ø{×B Ô쥤W ô ö:§Þ÷GŽ?ú{íôŠæÁÞ{æµu{)齂½ÎÌwwulüè8Ä^{e3—çbøîwÔK‡ñ‘”k¼fyHjãŠÐõUíô ]óXõWó4½ödK+P/­ðÇNðXÚ7’¯cióH¾÷¸4øÄP?¾Óxe!kãÖC×7VµÓ+tÍcÕ{¦èp-ÌòÔŽ½¶&æ”8Ÿµ58šKž¤§?—¼¯§¿ §Lž¤§¿ïëé/“_÷ôç’ÿèéoÃ(Ó(vƤÿì ImÜú,IíÐõUíô ]óXõž9®…ØkûÞ• Bèúƪvz…®y¬z1G?¥§¿o §¿Ì”ÈOééÏ%o §¿ Çóþq¼iÊŒçâxGüº§¿ÿÑÓ_fzñëžþ\ò=ým˜7ÐÓ_²p­ÿÑÓŸ(¼Y•ævg_÷ôg0ÿÑÓß±NõÀñSzú›ñzúËL‰ü”žþ\òzúÛp<ï|ïXÊŒçEãxÕ0{Å^gz…Y!öŽBÖ_ƒ'5$ý}0Ég6.þm!dýYyoX6ÝÄzèúƪvz…®y¬zÏœ ×Â,IíØkûìIðëžþfüGO™éů{úsÉôô·a Æ¥º¾±ª^¡k«Þo¬Ãµº¾±ª^¡k«Þ±y<ê{s¬´ºÃ±”Tj§W¬›ÇGï‘׎¿/qOóÖôôwl ì[ÖN „ÍcËÞC¿àžþDA+ãÿ±“­±îµÓ+ÖÍã£÷Ì'àñD˜¬ùžþ’²»5Ö}ã£vzźy|ôžùPüþ£§¿d?¥§?ƒy=ýëþ£§?Q8x²*G.°I.¿e!+ª¿å‡Uĺo|ÔN¯X7Þ3E‡koãþA]šæ`½HöbvÃtãp,±d“7PÈnÞГw¤û© b?¦$óß›1ñ÷f¼Ð%¹üs§òßa½ø<ͱ%eKúMëÔ:D$T–½Q»ûoê0ýý;F[RÁüWP™´–bÍü*b¡ü¿Ä‡ÍÑÆ‰b$ ʳµ§õWëÑŠ#¿ 4°kÔ­r#)h¢V©TúÀHPø¤ÇÝÓú+`4ƒ]2ËCR˜éBÒ÷† Ióô’ºCR š¨UªÞôRï_ªÊ w¾+Ý£¤£¤Óta-¬Éþ¡Ñ£O„ïÕÍÏ8 |¹'ïiý=õÛŽÁM'ÌŽÅ6ìü7¼¾WÇ`ŽÆD sq•òËõS›¸슫5?5cþû&ŠãÁë{u æhLa.œ¥¬¸*çXnø¿ñàõ½:s4¦0W)¾s¢ø7¼¾WÇ`ŽÆ>”òD½wü7¼¾WÇ`ŽÆD sq•â;'·ãÁë{u æhLa.®R|z¸ø¿ñàõ½:s4¦0÷»à°í×Ù1˜£’ |2®R‚€R ‘›ß¨á¯Áð/ñaœßQÇ`޶„ÁÚ7­“vTaœÂ'wö]ì…‚½oHú{íÔöæ!i콇¤÷`¯;$u{)!)%èIìëÅgè"á4ëE2¶¤lR~ùh´ •eÑ%L„ï¤gœ¾Üsïiý=ÈñmÇ`Žf ^á.E±ä1Î_rðú^ƒ9S˜¼â*å—ë÷6¹~ì$¿yÃIÿÞŒùï›Ðÿ½MÄÉÁë{u æhLaNðЫ߹2Aü—¼¾WÇ`ŽÆ6ÀÅ…_rðú^ƒ9SðЫ߹2kÉ/9x}¯ŽÁ)x'è9V¥—c3èü’ƒ×÷êÌј‚W\¥øÎÉÀ/9x}¯ŽÁ)Ì ^q•âÓ»2ë÷cá—¼¾WÇ`ŽÆ¼ôüï8þK^ß«c0Gc s‚W\¥øÎÉÀ/9x}¯ŽÁ)xÅUŠOïdûÍÛ/9x}¯ŽÁ)l0ƒØXŠcü%¯ïÕ1˜£1¯?¹Ã¶_gÇ`ŽvDØBØ6®R‚XŠáña·<~ÉÁë{u æhLÁ+®R°ð'wØöëìÌÑŽ˜ –ăRpñ›·_rðú^ƒ9SðN°$+eø%¯ïÕ1˜£1¯Ø¤”ߢ‡£~ÓŽÁí¨ÿ~Ð}¯:iLaåžÖß}ßì…ØXö¾±±o°×޵ƒ½yllì½ÇÆÞƒ€ºc‰ëF½´JÕ{}[ç"ûK^‡,”!ÝWK±f~¡uˆhÅ‘_¦¥X# ÛjuÐï#)#)£¤°^|žæÉÿC£˜M„ï.œqørè"Ù¿ñ{uK´wj×(é‚ÐBê4]X ëE²h³th­CD¢Ô¡ ®Vá{ÁÏ8 |ùBùÃQ£^¨ÿXŠ5ó(’Z‡ˆVÐÍ "zQ(Cº[µ:ȤÀzñºH8ÍADz%e“òƒî3tÓ)u(CjÅ´‰Š@—œß ”ŸqøòÓd›|ð:dÑA4°kæP$qÃ…^­CD«‚HÁ ÝB«ƒ~I!å±ÜBSoÆÒ÷z­fœ¾|$e”ºø<ͺ¾0¡<  &Â÷zýÌ8 |9JBÍ*Ö½ /ã{÷߸ ê"á4ê"Š$:Äíè&Â÷zÏ8 |ùíÉŠ#¿(…m÷›ñM¿ëeaôÄ‹ªA‡M~dÿÛ'bÿ“'Â÷z7?ã4ðå?RP„¦9­dÒ¿ü̵§=O*h=ú§~É4·8öÇßktû`²»a5˜‚ÌÌA­H¼¸{è1°ø<Íñ=­“žæx_O뤧9PÃa½HÆ–Ôc÷Dx÷¸{Z'MÜžâZÉz%8qü¼#‘T:~òŽS’˜ÕNq­d½œ8þÇNÞ-\öMq­d½ w£ûûð—8~wé¹÷D˜ï»ñÃFz|¯q™Ï6Jõå+®ûî+|”Ë|î• ›8–E‰ ×6ºßÊ÷ÕÊ„ÏZYŽþÞ°ûFDzlþ Ðo›kýÊqÃò¾žÖò»¦øTUëWŽvGÊûz"|y_O„':–eóüGO„î‰á x_O„n´,ƒþ^læO%[w,Ëæƒ\Û¸Íï3ìì“_yéïHgþ4P2SyéÿÌÌÛή²ŽöcW\Ÿ4­„èß°ŽeÁŒÿè‰ðõÁÊì öÕÊìœV–d3Ø7Îü9–…NûÇ´’óì’V+!úŽeÙü x_OëGùý®ß6×ú•#ŵ’õ&ÜBŠOU¢Ö¯7,ïë‰ÐÆ4ð‘üXãŸâ_P«õ+žÏr\é Ëûz"´=в<ø^„fÖ5P²õ™ßä¾F«V+!ú3ë(™I-c»Wð=Ú>˜¨ûj⦖ä ö3ë(™9J ëÅgP §¹ð¾žßin¼¯'B{𻱿?zZ'móå¬ä>Š¥°rùì$süÍ„ÿø47ÍQ;f–ó¾žÖI;–г§9Þ×ÓúQ¦¹Vƒåùú²Tóì³âo– ÿ5Íyò6i6óô´çéwx¦òžö<ÇžyzÚó%|ð-2:øœ:øëÁ·È|‹ |ð-2tƒo‘a¾Eç¹·GÿT¦íiÏsnpftpf¨ƒ»0c=¸ 3wa>¸ 3tƒ»0ÃÌÜÓžç è2ç<í©7I:þ¦÷1Îäozã8þ¦—•Œò7½¬d¨üM/+kþ¦—•Œþ¦—• œ¿ée…Óñ7½¬d˜üM/+\æéiO½–F Ë¤xÚS¯Cðvšƒ.¬…õ"™Çnþ)Ͻ§õ»MšM%žÈbRÍü½‰þEOÜ7¬c¿¢÷M ÿ~4ÅŸ žöÔë33÷´§^‡˜™¯§=õ:„ÀÌO{êuÁÓë_F3OO{ž¢"HçØ—ùzÚS¯Cø›^ÿ2Tþ¦×?NÍ<=íyŠŠÀäozýãš žöÔëáþÝH·'XêO]™§'óõ´§^‡˜™§§=OQ˜¿>¬Ÿ¡‹„ÓÜbøÂZX/’¥ùÿìŸM‰'²˜Ts½€¿7L?¦×c<íiýcw ÎÝõµŸ…9†dÎô¸Û¤ÙLð´§^‡˜™¹§=õ:„ÀÌ|=í©×!¦çÞýS™àiO½!Ðñ7½þeÔá%džžö¤á÷ªÒY®’­¤,>Os¿>AO„¿ŒùXÍÞüä½aש﷥hýÊ1ÍÍ€÷õD˜j /r¨’ž¯iº8Öãî‰0Ýw£ubµ’õ&Ü*¯ê}†E·ýÿ¶ 7ì..>Os¸èoët,Y/taðâX”úmÜ@ì²Ú)1›‡¬÷Ýp“ͬ–!?L¤’q~ËBÖï«ÖEëÉãh"5úx’h¢Ö§$øMÔʤƒÓƒ&j}¤âÑD­H MÔê:ÃI£‰Z7±·Ž&jšJ|+š¨U>&\ MÔºœ!¸MÔúÜðÏ5Ú»ãâ3tAhšC‘’òcWf-޾Á^;½b y°÷~c®Ï[ÈÎÅ@ß`¯^1Ð<Ø{ß „kíèìµÓ+š{ï¼Bv‹pm}ƒ½vzÅ@ó`ï= ô ×Þc o°×N¯h콇ÚáZw ô öÚé̓½÷0Ð<\K‰¾Á^;½b y°÷⽇\½b o°×N¯hì½ó ÙA…k1Pw°—’^1Ð+ØëÌp«¨;ØKI¯èìuf¾»ë ;Í- †.ŽõÜ{ZGD©ßD»Dÿç8Œ¤üØ•ù>4öÂd›0ûÆ^;½Âl{ïqõÝ7†Ù7öÚéfóØ{«6ïëiþ>mÌ1†‰"̾±×N¯0›ÇÞûu¸ðÁÂì{íô ³yì½gŠ×B˜uÇ^Jz…Ù+ö:3ßÝ5̾±×N¯0›ÇÞ{8 +óuØc÷D(Æ#ìiþ¯oï#Ù÷®¸"õÙ+óå+8ß}ÅÁ'|sŽe~³oìµÓ+Ìæ±÷~c®…}uðú^Ås4¦€d˜}c¯^a6½÷€ °v˜uÇ^Jz…Ù+ö:§æû£ß"Žebü/¿'&j=âð¿ÉÊ’0ûÆ^;½Âl{ïá,¬LàÌ™ºÈîɶý:‹æhGQGàXæ²0ûÆ^;½Âl{ï7ÖáZØ“?lûuÌÑŽ¢zæÛR‚~ßàk§âA¿yð½DŒ2P $Ø}uðú^Ås4¦ Ò£¶¥h I€Í±î•’±WÈêÜßD;–‰ñ¿üž<š¨õˆÃÿ&+³O ³oìµÓ+Ìæ±÷ÎÂÊ,sæ†.²{ò‡m¿Îb€9ÚATÏ|[ øöù=y4Që‡ÿMVffߨk§W˜Ícï=œ…É®`ÎÜÐEvOþ°í×Y 0G;"ˆê™oK ú}ƒ¯ŠýæÁ÷>1Ê@I4Hn2Jfݱ—’^aöнÎL'¾e˜}c¯^a6½÷L$õfݱ—’^aöнÎÌwwý@Ÿ„¦9I—öbøÞW‡ñ‘”;Ù&ú{íôŠæÁÞûu¸b î`/%½b W°×™iï®Z7ã[c î`/%½b W°×™ùî®7ì4·0º8–M Jý6®K{1ü y®ÃøHÊ]™ïXì… {˜}c¯^a6½÷¸úò¾žÖ ³oìµÓ+Ìæ±÷WmÞ×Óú~ §uYÔ"y$zZò¯V}`T}ïŠk¢Ï^™ „cEp æ ….²¨ÿžö´®Lƒä&£¤ž{Oëâ ælzΓô´ž<ïëiÝ1eò$=­û-ïëi}…fߨk§W˜Ícï=Snfߨk§W˜ÍcïýÆ:\ aö½vz…Ù<öÞÃYX™69̾±×N¯0›ÇÞ;”‘-¯8Âèi}áùˆ9â Œlcö'ÊàÊGò}ãêkèº2߬‚ù³óSzZ÷[Þ@Oë«”ÈOéi=yÞ@OëŽÂ#~žÖ“çéö´î˜áéiÝoyº=­¯bÕ ±±o¬Šú`¢Z;]d‰ó¨H ’ãFI¡ kwýƒÊn†Ö$˜\èÔï|•á¯âˆ§=­KÕj©Ó¿~ÝÓúgþ£§u–ôâ×=­“ùžÖ¿Ø7dµSb 6YïqâxáÿX™‡Ù7öÚéfóØ{» $Æ\IHNnaö½vz…Ù<öÞ3E‡k!̾±×N¯0›ÇÞ{8 “]ë¦ âéiO·§õ‡Ó#ü =­oާÛÓú4®ÿD{²5̾±×N¯0›ÇÞ{¦h\<̾±×N¯0›ÇÞ{æQq«0ûÆ^;½Âl{ï™ÂµðLž ³oìµÓ+Ìæ±÷ÎÂdk˜uÇ^Jz…Ù+ö:3mÜ*̺c/%½Âì{™ïî:Sªõè˜ýßC¥BŸ„¦9èâX¶¤ž¼§õÇPê·'»´ÿ@¡hà0>’òcWæK ³oìµÓ+Ìæ±÷WßïUP\åê Ÿ¥LÞ×Ó:uþ£¥÷ û^ŒÆL/´›Îgk ”lÿèi:¿îiÃkeÂ'Âì{íô ³yì½+Ž„³nUZ•V¥UiE‚z‡ì)PÙTÆ·/q*›JdÎæª´*­J«Ò!Ô†ì)PYd‡ŒAeÃù)=­û-o §õUJä§ô´žÂ­Âì{íô ³yì½gŠ×ÂÿäÉ*̾±×N¯0›ÇÞ{Øc®$ædk˜uÇ^Jz…Ù+ö:3mÜ*̺c/%½Âì{™ïî:[k dæìÜÌôÐztÌ~¤£¤ÐÅçiº8–MŠÇÝÓºž Hº¼/†á}5pIù±+³Hc o°×N¯hì½ßX‡kA«¥>æƒÉdß}rš| Ã¡‹áZ-u°QRä÷-þÆ|&'||BÃIžÐa£øLN»dvâÖ}zL¬kõ[ƒ|0ÉgzàÂL]rÃâvÉŒ’B ô öÚé̓½÷]®µc o°×N¯h콇¬®Íc î`/%½b W°×™yÇ­b î`/%½b W°×™ùî®7ìH tñyšƒ.ŽE©ßž|Œ½HöûÕÀa|$åÇ®ÌMÁœ¡ÐEõßÓžÖ“jÜd”Ôc÷´.®`ÎÆCYâ<*Rƒä¸QÒ™sGzÃÒ¯¤žÖ?bOëI—À+©§uÖü{Zöìå}=­#ãiOëÈð$=­Ë-ïëi$eò$=­; ïëi]Ξö´žTëcì?ü”žÖYózZvJä§ô´þi¼žÖ-CúòÚéc1¤y ï=ˆ…xBáÇN¶ ³oìµÓ+Ìæ±÷ÎÂÄ­õèÜ*̺c/%½Âì{™ïîú?yâžÉIëÑ1û‘BŸ§96)(’ÇÜɾð¾8Œ¤üØÉv4t‘L«e£¤È—íÏhÝ´œCDíèìµÓ+š{ï!Wó¨;ØKI¯èìufÞq«¨;ØKI¯èìuf¾»ë ;’2ͱIA‘<&_$ûÂûjà0>’òcWf½Ój©ù`’$š ºH¦ÕR%E¾l&¡b’'$l\kÝ´œ3ßp­u{)齂½ÎÌ_Å@ß`¯^1Ð<Ø{Ï<*n}ƒ½vzÅ@ó`ï=“®…¨;ØKI¯èìufÚ¸U Ô쥤W ô ö:3ßÝõ†I™æ ‹cQê·'c/’}á}5pIù±+³GS0g(t‘Eý÷´§uÐ ¹É(©Çîi]\Áœ‡.²ÄyT¤Éq£¤3±é iWROëOäŸØÓº5éx%õ´N—bOë¤f/ïëi#O{ZLj'éi]nx_Oë"S&OÒÓº÷¼¯§u:žö´îZcÿ ä§ô´N—7ÐÓ:©”ÈOéiý‰¼žÖ­‰!}yíô±Ò<÷ÄB<¡ðc'Û„Ù7öÚéfóØ{gaâÖzô nfݱ—’^aöнÎÌwwýŸæƒ‰Ø ËûzZGÊûz"|lFÙ„ž èOÒÓú“y_Oë.L™Æ®õèÿʬ/ú{íôŠæÁÞ{8 +³òùuOë¢ä?zZßPzñëžÖŸÌô´î¸úúò‰R­›¶’ZкizBR+­›ÖBRËZ7­DHºU릒´]jHz÷ñÁd­›¶’µnšžT¡uÓZHʦuÓJ„¤†Z7ýÐÔŸÖ£K I­ßÆÅ_åA¸;ð}Ìí=¬8½.ÿ#)°^|†.NsÐ…µ°^$KÝ覡 ©µ"bâÇ÷´¾9^¼§õéTœßÓ:5^¼§õ‡Ñ}~|Oë–æÅ{Z×¥âüøžÖ—òâ=­Óø=­SÉ“êiýÓÓ|‚žÖEó¤zZ—û—`óѦ‰ãù=­?”'ÕÓ:²‘¯x_OëKÄö0ÏÞÓºÇñì=­{q:œæiOëK¤U¡ò=­{±˜Û­øžÖìFRyöžÖ½2Ì‘Æô´îåùŠ÷õ´¾Dlóì=­{qÏÞÓº§ûVÉÕ`¨MRo¬Xç×=­‹æ?zZ—{zñëžÖ©ä?zZÿt”9Ù„gïiÝ+ƒãÙ{Z÷ÊÐ¥‰°^|†.Ns³“÷õ´þØlÞ×ÓºžÜ°¼¯§usšã×=­‹’ÿèi}Céů{Z2ÿÑÓº ãª{šã×=­‹’ÿèi}Céů{Z2ÿÑÓº ã*ešã×=­‹’ÿèi}Céů{Z2ÿÑÓº ãê5ÍñëžÖEÉô´¾¡ôâ×=­?™ÿèiÝ…qÕ9ÍÅ@ß`¯^1Ð<Ø{¿±×B ô öÚé̓½÷iŽ_÷´.Jþ£§õ ¥¿îiýÉüGOë.Œ«Â4§`þHù)=­oŽ7ÐÓútJä§ô´N7ÐÓúãiÒÓº(y=­o(%òSzZ2o §u†Ò¾A¸v*J›áÞƒXº…;Í…Ù7öÚéfóØ{Ïd†k!̺c/%½Âì{™ö4ǯ{Z%ÿÑÓú†Ò‹_÷´þdþ£§uÆÕÑ4ÇûzZד0ûÆ^;½Âl{ïqx_Oëæ4Ç“ô´þ¬¼¯§õ¹”É“ô´NÞ×Óú =íiÝo¦9PÉÐ Kzèy>q"xòžvlÒ‡zèyz"xòžýÖ±IêA¢§Þ$'‚'ï‰p娤õ ÑS¯¥'‚'ï‰0yÇ&}¨‰žzŸ™ž¼'BGÇ&}¨‰žzš\ž¼'BKÅ•ð Ñó9¹¹3ƒºH8ͺëE²ŸúŸê=o¬\ÿ®•äúåÿX/>ƒºH8Í-Žý©ß0ýÞX¹þ]+É“~ù¿dñyšûº¡¿„Z¢ŒïÍ›| ó²’ý÷ina-¬É~ù¬äZ! ëÅçEÂinð¦cÁxðæà2̵×Ƕ·x±o«ÄCYL%JÈü¡›ƒ?à̵×Ƕ·x±o;Íý®‡mSÉ*OÒᦦ¹/ænؘÕþ ü¶nYö´µùoѵoZë´£z,²O">ùòäYª‚ùKYÇûzZ߈nñK´¢÷§þï‹Ï BÐEÂiº°vaðâX³_þ±öA÷½:…ÆD7 —ïiý¨’Ê'Û(ü‘×¼_=jÒÈ‹\+FG{`EÖñ¾žÖºë“u¼¯§õè¿Dë zêÿ¾ø¼ ]$œæÇþÿÿè†é ;päòz"ÔŒn½¶cI6þl‡Í}GB[y€ÿèiý¨+’Ê™<ÀÖÓºž’øžÖ7n²2‹¥ó®¸Š ðƒõ´®§žö´~TAäÓfpäþ£§õ£JžåANà}=­õ7m7;É:Þ×ÓºžÞ°dïëi}£ò"׊ÑÑädïëiý¨?R­¢"=lîûÖ:M©V¤ÚS”q0²Éô´>”omO6ùžÖ‡òáÆ­d“ÿèi}(Ó(|/вÙx |y¬ê&›¼¯§u‰§=­Ë!$>m†éiOër‰O›¡’Mþ£§õ£J }VÆmBWJ¬z¥WèêŒU²É“ô´žD²ÉûzZ·8e’Mž¤§õäÈ&ïëiÝ ²Éô´~T‰¡ïŸ<ºÎǪcéºZˆU­È&OÒÓúT²ÉûzZž2É&OÒÓú8’MÞ×Óz6V%’MÞ×Óºµd“ÿèiÝÚË;O{Z—CH|ZÎ$›üGOëG•ú 'b±ªJ6y_Oë˜pá†%›¼¯§uLÿƒƒî{Õ:)ˆŒ«¹¡wêZ‘.©¼Èµb„u,Ær‘Mþ£§õ£J }ªQŒlò=­Ub賚\Zý&IÇb,Ùä?zZ?ªÄÐg8!­Õo’$›üGOë]qM `’Mþ£§u‹BY­H·t,ÆÞ‘Mþ£§u‹o &Ùä?zZ·(t‘ÕŠtK ’›Œ’’Mþ£§õ£J }ÿdSd“ÿèiÝZ#MÁ$›üGOëÖBÙÐÕr¬º5½B×C¬zûŸpXúuÖ:íˆ ê>ŠB*c}CQ[q„lòzZ·¨ ú%Ñ ¹É()Ùä?zZ?ªÄЗ¯¸ÊÓžÖ*$FòçÓ>Îݶ{ÚÓº%B"T–…Ê8Å‘ß?Úž£›DÊíâ»\BŽñK5‘lò=­["Á@6y_Oë–ܰd“÷õ´î?Ùä×=­5ñ‰mÅ$›/¤Ns .†Ãz‘ì‹yϽ}RÌ:úGÿ&‡éï_ë´%Q9„AbÅBV4%bÕu~¿Å¬B v,dµ±V!«åý³ÞC‰ŽÅ:…6Ùä?zZ?ªÄЗ¯ÌnDÇ)|2†éX°Z )1†©U°ZË.ª,æ /3†©¬v”c˜þÁjSUÕÝL 7Ö⣣ôŠuÿøhú´j{O¸2*ŽSød tk°÷^1Ð[°—¸;Ci… ÜQ*Jûá¦O×j(­X.šŠ‡ÒªA¸óO×\qv äE®#,:Ná“¡´Bî(¥ýƒpÓ§ë 5*ìEÓ+ª{ço¬w× ŸOš>m†ëŠÅGÑôŠuÕøèüõîJ6ùžÖå8l|7|>ñ\qw9‚\!áeL’lò=­Ë!qظŒlò=­Ë!qØ—·ÑŠ‘7²Éô´î›ß¾F7èu…øè(½bÝ?>š>m†ºap¬5˜žx®¸»‡lò=­Ë!qxÅÕ"d“ÿèi]‰Ã¾|Åu²Éô´.‡ÄaßeTB+FÞÈ&ÿÑÓºÿAlnxœ*O6ùžÖ*1ô·’Mþ£§u9$Ÿ|°&›¼¯§u9ðÉì$›¼¯§u‹Þ°d“÷õ´îÿ È&ïëi]B6ùžÖ*y–Vd“÷õ´~T²Éô´.‡ä“>È&ïëi]²Éô´~T‰¡ïZ™¥’lò=­µâJ/Ùä?zZ÷Å#²Éô´.ÇJdI‘M~ÝÓúQÇæÆ˜‰ötß«nÓ=­''ø/<ß!œ…”Ð7œµL²Éô´î¿ð\q„lòzZJKáÐ#G²Éô´.‡ä“Öd“÷õ´.‡§=­Ë!$>-ÇiÅÈۮƭ^¡ö[mE²É‹÷´þ $ž÷ãµSæþ<ŽnX²ÉûzZÿÙä?zZ—c^_‚ú„ÕÚ–ã ;p“\?ëBöÁó¨-]ÒÉ—lò=­Ë!ù䃜lò¾žÖå¸aÉ&ïëiýèf½±®¥W¬ûÇGÓ§ÍPƒÜ‘VQ ;ÖšD²Éô´.Ëmû`Z‘&Ö­Õoü"›üGOëÏòöãd“ÿèi]É'¨d“÷õ´.î!›üGOëG•ú¬&ƒiEš`X·V¿ð‹lò=­?ËØ“Mþ£§u9$Ÿ| ’MÞ×Óº8=Z‘&Ö­Õoü"›üGOëÏ"Ñ»'›üGOërH>ùà"›¼¯§u9È&ÿÑÓúQ%ÇMÞ×ÓúQÑÍzc]!>:J¯X÷¦O›¡:ÖLd“ÿèiý¨›lòëžÖ•lx­ðZC6ùžÖ*±É&/OOëJ¾!‰ä©«Ú¨—0•eªgÙRb@ ÃYd“ïiý¨c“Ë û@û uÓxÚÓzrB¢@²¿!Rñxû†PûgW\åóJñÙþl²Éô´~T‰²É¯{Z?jâ1ÛŠI6y’žÖ •eĺB|t”^±îMŸ6C%›üGOëG•Ød“_÷´®dÃk¥9È&ÿÑÓúQ%†>u‚ÐÓžÖ埖SÉ&ÿÑÓº’O>ø ›¼¯§u9k &²Éô´~T‰¡ï2Š‘Mþ£§õ£J }ÿäF6ùžÖå|ò!Ùä}=­ËáXk0‘Mþ£§õ£J }V“Ùä?zZ—CòÉk²ÉûzZ—ñÖ`"›üGOëG•ún“Ùä?zZ—CòÉk²ÉûzZ—ñÖ`"›üGOëG•ú '7²Éô´.‡ä“ É&ïëi]ÇZƒiçc­Á„n&Ðë ñÑQzź|4}Ú 5Ö⣣ôŠuÿøhºÛö'rÃîH’Mþ£§u9$Ÿ|“MÞ×Óº޵º™@o¬+ÄGGéëþñÑôi3Ô w¤UT¤?Z1òF6ùžÖýbsÃãT·’Mþ£§u9$Ÿ|€lò¾žÖåÀ7E6ùžÖåxÆ­©¼¸J6ùžÖå`¹m´"•W«EÜ8A²Éô´.‡ä“’MÞ×Óºø:Èi•ùø䎴ÒÙøˆ ßK˜Ÿ×À—?ñ]™ýÙä?zZ—Câ°áÊ„A‘Mþ£§uK$È&ïëiݲÉô´~Ô±cX‰…ß•lò=­õû«ÁPŸ ›¼¯§õ£’Mþ£§u9Xöí›"›üGOëG•˜>|{’d“ÿèiÝ ²ÉûzZ·ä†%›¼¯§uÿÉ&ïëiý¿+Ùä?zZ—ãû¾¹C}‚lò¾žÖåð´§u9„ħåêX:¹aÉ&ïëi]OÉ&ÿÑÓúQW"á׊tI äE®#¬cé<”lò=­ë©ï]™ÅäŽ´ŠŠT°$–Uˆg:JÅcYÿx¦éÓu†J6ùžÖ-{0†•P°ð»’Mþ£§uK¾om †úÙä}=­[B6ùžÖ-{0†•PÈ&¿îiÝ’y<‘bxm×È&ÿÑÓúQ%ÇMÞ×ÓúQÉ&ÿÑÓú'<ÐÓºÿ‚ÿ»zcîÏoX²ÉûzZÿÄïJ6ùžÖåø¾o®ÁPŸ ›¼¯§u9<íi]!ñi¹jµˆÿ˜Ùä}=­ë)Ùä?zZ?ªC¿V¤Kj /r­aƒÜ‘ÖÍõ=ô‡õâ3¨‹„Ó¬Éþ nãþÚáá¯X§Ð†"›üº§õ£&š$ëÉE6ùåö´nÑS‡éï_ë´%=V…쓯"ôË–^×0ôëlòzZ÷ÿ°ô@͹ô0u_ÏRñXÖ7ž©-(·‹ïrŲ ñLG©x,ëÏ4}ºÎP=íi]!ñi38O{Z÷_HÜ®°»zÚÓú'„ÄÝé »ëì…õâó4·°Ö‹dž{!û¤_þáá¯X§Ð†"›üº§u9æñDôý›¦¿­Ó–D7èu…øè(½bÝ?>š>m†J6ùžÖý?èÈ&o §u‹–²-õ§²²Ísã€VŒ¶‰uÅâ£hzźj|tþi¹’Mþ£§u9$Ÿ|p#›¼¯§u9ð›VŒ¶!›üGOërH>ùÀŠlò¾žÖåàozÇåLþ¦7‘3ù›^ÿ9S+FÖ–ÄòU„eK‰±| ÿúÛ‰»ëó×9²Éô´îÿÊìq²Éô´.ñÁʬ\d“ÿèiÓä1­Y³_ÝHG6y=­[ô°”m©?ÕÒVd“ÿèiݢؓlò¾žÖ-ŠÓ´úwÄy²ÉèiÝáù~alò¾žÖ-Šo£Õo¼#›¼žÖ1í—¡ë°cd“ÿèiÙä}=­[WµbdÍ~alò¾žÖ-êiOëþ ‰»ÓvWO{Zÿ„¸;]aw½°^|^$œæÿtß«ÖiLZÈ>) B8;J‰ýÃÙtW(o'Ÿ*ÿfEÈ=P!œ¥Ä€þálº?ûw<оB­ÓxÆ Ù'źîø(%½bÝ+>êD}¯ ñÑ»J¶þo\šæ`½Hæ¹²Oúå{þ¦¿­Ó–, ýÞ5P23ð ¡ßQzÞ?ô›ºX–õvÒ¿ä û^µNc ^ÂÙQJ èΦûMâr?~ìû[|˜þþµN[R°D«ß<éßa½ø<ͺëE2O^È>é†éððá3ªd²Žÿè‰P[qqxøûÖ:¾@oÈS!üê(% äé~5}:Áý£Û¿ø›ÞǸÜÍßô>Æé~‰ÑíƒÉ 2/O{êM¿2מöÔ›¤Ö¤ÙÃÃß·ÖiôzCž áWG)!Oÿð«éÓŽ»aÉ:Þס&è¼+Ý9Ö‹Ï .Ns°^$c“òË?<|øŒ*™¬ã×=j+³Öþ¾µN£¯©öÏàþäŒnº0¹áBæåiO½IâWæÚÓžz“ÔzôOý£Û¿ø›ÞÇ2p7ÓûX†îððW¬ÖiC‘M~|!+!›¼x!›\*N6ùñ…¬Ad“/d“øtÍq3 ëx_O„šÚͱ³y ”l]y‘£ÌÉÖo;^–ÅÝ2ŽM'ËʸɱZ7?0’ëÅçiÔÅpX/’±%õä…ì“n˜Gý-JÖñ=j+T­HE<Èà~‰ÑíƒÉ 2sO{êM¿2UO{êMRëÑ?õKŒnLTüâozãÖ¸›¿é}ŒÃ¿P'®ý£Û¿ø›ÞÇ2Ö¸›¿é},ÿ¶ãÙ¾¸Ààú²éÙ¾ ®ï$ûKŒnLTüâozãÖ¸›¿é}ŒÃ¿íxYwË86,+ã&Dz-Mç'rþ%F·&7\ÈÌ=í©7IüÊT=í©7I­I³¿ÄèöÁä† 7,¯êM’e¿nX^Õ›$‹îñ g ©ˆ§÷c'ªÚ%øˆÍk dë¸pÃ’u¼¯'BM>ÐyW:½GIa½ø ê"á4÷íYÇ:ÐÅoOp¬]ü“îq²Of…Ø;J¯0ûÇÞôÆzw ú‚ï(úýƒoútÁÍÎiެãéö´®d²Ž§ÛÓúPdO·§u YÇô´n‰äY¦9²Ž÷õ´nI˜bï(½Âì{Ó©öGaV,ö¢éfÕØ;¿»ò7½¢È LsaVˆ½£ô ³ìM3]w× _!øŽRñ ß?ø¦O׺ _!øŽRñ ß?ø¦O×xЯ|G©xÐï|Ó§ëŒuЯ|G©xÐï|Ó§kΜæþ‡h_¡Öi „ÍǺB|t”^±îM§9t3ÞXWˆŽÒ+Öýã£éÓfà±®X|M¯XWÎ?mF§9²Éô´~Ô±cX‰}Ð}¯ºiLô´žœà¿ðhš#›.Ùä}=­?iš#›üº§õqÉ&ÿÑÓú“Ò‹lòëžÖE‘lò=­'Ns¿ÜÔ¾i­ÓŽ*ŒCÐ*„cG©xÐú‡cÓ§kN´ áØQ*´þáØtš‹e♎RñXÖ?žiútDz ñLG©x,ëÏ4}ºÎXOs¿AÚW¨uZÞ@¢B,·£A Ñ?–Ûtšûtß«ÖiLAäá,¤Ä€¾á¬>¥EòýSb,mÉWlš#¯y„…¬„¼æ·,d“K#¯y„…¬Aä5¿e!›Äiî7è@û µNK ÐòT¿:J yú‡_M§9ÔK˜ ¨"œeK‰5 gý‘Mþ£§uKñÁ4G6ùžÖ*y–9Ùä}=­õ†%›¼¯§õÓÙä?zZ—CòI²ÉûzZ—ÃÓžÖ埖ë4ê"›tƒg“3AëÜ ä¥´B8öMŃV;›I¯ðÕ™2ƒ¤îð•‚‹Z²á-B¿cû û»2_꾟½2_ûä!¡ß±yýƒÎç6’ö F%|ùŠëC}[®¸êõuÅ»ïZq`úÞ•ù>ãØ'+½=9ôká cÓ}×]ºlÙ#—ÿ(LUtΣ¶¢Dš|g¶cŸü)|l6~—ìú>Xq¯ºaKûä£ÍÆ;öÉŸšÉãØ'jvsì“?µá-fËÝ%Ž}òÑnOýZ8èþ®Lø”O߸oXã€o™qKßòÆnDèóϸö†¾‰ñæ£j´òm7ª¾yã˜O5ŠùÆV&`Œ»g©cŸü©™Û±OþÔíÉ¡_ Ýw݇X6þÀ‘˦"?èœGm74$ùÎ.Ç>ùS3Á±OþÔ†·˜-w—8öÉG»=9ðRB¿Wz^gèWgae>;ì’Я…ƒîïÊ„ßk|`tûº2aüìàØ'êöäЯ…ƒîïÊ1·újó9b¾Ë(øP£Û÷®Ì&›űOþÔíÉ¡_ Ýß•ÙDŸjTêCƒù.£¸b$í«q¼OÌhÝ—¾ß¸Õ72Žù£˜i|w£Ûç\™%5#:öÉŸš-w—^Jè÷J¯Àë ý A`!ðŽB¿zÞ4ô+–ùî®8ùöäÐïØ¼þAç£g2$ôká û®«§¿ë¢Æ$¿ë¦Fýïº#ã²ïº‚ÑáïºN#ÁïºwãÀw]0Š×}¿ë“i´þ]WÌxû®ûÇ¾ëæÆÿ»®mt×¥?Ég#<ßð³åî’Ðt¶˜l¹“}ËeE õÒjùø/’ÏHï3:[L¬v²o¹¬ˆ¡^X2¨_þ$Ÿ]ûìÇ4Áw²o¹]!V}Ó+tµcÕ|ÿî®»ô5꥕ IþÔci!’ï›ciíH¾yïìæ-t¥ÄªWz…®ÎXUù…Ðu«þ麦±ªXæ»»þ2ÔKë ý>’ê‚Ð4·H¶0˜M J}Å•”Ö¹Íþ¡Ñdè³Ä0…`µoJŒaj«ÍwâþL–b!d}Sb ÖYó öN÷ä·qñWyø]°K£‰Ô¼ýCb!d}Sb ÖYó8Þ;î°÷ï„`˜…Øû¦W˜µcoWï¾×h¸Kg¾h"5ÒÿÐ>˜¨ÊÛßë]6ShàËñ÷6îï4Që ý>’² ´:ÍÁz‘ ¥ÉÕýZsã6ÖØFÂÖŒ7âÖÈcœXs³&5Ò¬WÖFÍš›qˆ5n#o ÑÈYs0~¶†ôʬ\¸gô|T­óÖàžÑóQµn±%Ξª•m÷6z>ªrÆ75z>ª[¸ˆÓFÏGÕ`“\ô|T h¤pzFÏGÕª‡K|›ÑóQ5PЋ‹žªÕÛ§ðõèù¨ZcÇ·¢‰£ç£j€˜ ÐÄÑóQµn¥ýCã„Ã,%ö^éf±WÈÜ"\ av{ÿô ³iìË|w×۸ߋbÑ™B_þ;™³£Øû§W˜Mc¯Ø~ù>†uxß}0ÙfG±÷O¯0›Æ^±ÌÜ*ÌŽbïŸ^a6½b™w×ïuÝf |ù¿Ö‹ÏÓ¬ÉPê?kã]0Nè¾Æ®½2aKÇ>놴Îmæ{Wf?õ} ÈK_ëÜ1Ë^âx6ÚÊìÑÃ,%ö^éf±WÈü¤cŸõÙß· ¼ôµÎ³ì%Žg£­Ì MÌ|µÎ ù^W8öY³ß· ¼ôµÎ³ì%Žg£M‡YJì½Ò+Ì:c¯ùÉp-„ÙQìýÓ+̦±W,óÝ]ÑD­sý^—|\*š¨un3ß;‘f)±÷J¯0댽B&•áZ³£Øû§W˜Mc¯Xæ»»¢‰Ôôq<ÌRbï•^aÖ{…L*õfG±÷O¯0›Æ^±ÌwwE©Y}¯kÐ3…¾©†õâó4]‹R×:·™Ï6*öÝFÁgÁøû¬&?îÖ@N+<תçÍÐÄkj¡‹ÏÓtq,J}—Ñ£@ׂQŒ®ÕDůÿ¸Åq÷\ë 6&ÞXSsCŸ§¹…ÁÐű(õß'ýBð}Sñ _;øæŠ%ãÚaMótÉÀ¸Öô ú…àû¦âA¿vðÍwÄ}}÷QðY0Šù¬&*~ý^Twÿ^Z—è‚.>/Ms(’;«tÁ(е`£k5±Â¯ÿ¸àîÿ¸Ö%2ü7Áßcà0Í…²B,ú¦BYíX4éºcS¥¤Åt¯ØTŠ9Ϻíѧ¹iº8–M Jý[~ìÊ´7‰½°2mïn¢Ãì{íô ³yì½ïNº`£û®¸w§›¯¸W$Ý»c—”³ËùÅð/ ‰8Œ¤üØ•i/ ³oìµÓ+Ìæ±÷~c®…0ûÆ^;½Âl{ïSóxÔW«ç˜Ø@ì²Ú)1›‡¬÷8^;̾±×N¯0›ÇÞ{f®ÍÃì{íô ³yì½gêµ÷0ûÆ^;½Âl{ï™Kõn­d±@ì²Ú)1›‡¬÷8^J˜}c¯^a6½÷ÌãáÚ+̾±×N¯0›ÇÞ{&áp­3̾±×N¯0›ÇÞûu¸V³oìµÓ+Ìæ±÷žéÀpí(̾±×N¯0›ÇÞ{¦;\û‡Ù7öÚéfóØ{ŸJŒGMÃì{íô ³yì½OuÀxT±0ûÆ^;½Âl{ïáZ4̾±×N¯0›ÇÞûTKGUì;öRÒ+Ì^±×9u~ä± Ëó³- „PXqpôÙr bßÕN‰Ø’òcWf‡ ³oìµÓ+Ìæ±÷~c®…0뎽”ô ³Wìuf¾»ë 4°2»¤h—ùbøÐD ÆGR~ìÊìqt8t1\«e£¤ÈåÙý ³oìµÓ+Ìæ±÷~c®…0ûÆ^;½Âl{ïdß0ûÆ^;½Âl{ïA`í0뎽”ô ³Wìufæø–aö½vz…Ù<öÞ3·×B˜uÇ^Jz…Ù+ö:3ßÝõ0’]|žæ~$µÊ‹ý ̾Zåeÿ‘¼Ùkryº=1dü¢/A §¹Å±ž¼'í§®U^ì/ÿàõ½*À)ÌiÀ¾Iè–®î Û-5`ßWíyý„×dà7è°ÙW¨s´½°o>>jÏë ×ïµQ~ߺ5êè¶q¼öoÑÃQ¿i˜£UÐsè ±ê›^¡««æZŸ…~è ±ê›^¡««æZ?ñ9t…XõM¯ÐÕŽUsôÑ¿k0FGÁ ¯Ú±±yÊ ¯Blìûc2æt`’kÀ*ýÅçiN«¼ØŸÀ̱ÅÔƒ×÷ªs4¦çÞƒ&lºB¬ú¦WèjǪ9êÝ÷zŸ¢[Üú#Õ*ïØHŠ‚^µccó”^…ØØwšûm­òb^‡,¹¼’zbȘæ´Ê‹=x²äò=1dLsÐ…Á‹cÙ¤xòž´}ö§èÑG=LÖñHôıKþO6û `Ž–@ 7>Q; jž&ˆO ¾?5£Â0ô ©xЯ|óïÔ;IRëƒg±vröai vÌÿc’³­Ž„ÿÓú€Ê Öž÷ï°4ÌŽÅ^ ß·n ,e,;’ZÐú€Ê –rXÉÕžk}l'‡¤÷Ù’ºµ>x¶g¾-%ü¯3’« ÿK‰äzi}@¥g¾í–áÓH®bé‘ð¿£H®ÿOmbøo]sÜ ëâšÅ|üŠYÓcí6fýæ/Ñ*ï€Vuá÷Ê¥3-ÜúK´>Ë ÊÈVlÅãĬèêŽU)éº^±ªs¿:6¯CºÈ†®o¬j§WèšÇª÷Q}ep‹·y½§âñÖB)(Ɉ©Ar“QÒÐÕ«RÒ+t½bUç~aõBÒ‹±æ¡è=±ºCQJøºå/Ñ*ﳃk eHWLƒ±Í Aî¿2í¡ë«Úéºæ±ê=s.\ ¡«;V¥¤WèzŪÎÌww½ysì§ë‚\ +ÎòK^‡¬0·;W$}äZðÑð—¼YaÎŲ趋ý’ƒ×!+̹Xs{~ó†ý6; è}ºæÌ§ë îXø½­LûººcUJz…®W¬êÌ|w× ×‚&â¡ë«Úéºæ±êýÆ:\ ?&£›¬Ìw£¬Òwì§ë|Œ—O„1÷!9¹…®o¬j§WèšÇª÷øÖnº¾±ª^¡k«Þ3Ïp-8öÓuA®…÷R?Övì§ëJôãXû†YÓ_rðú^µs4¦ Ý6õ ×Úé‘ýQ¸bUX™Å4;Ù/XÔK‡ñ‘”˜5MýÿouvƬé÷º_3 øò 7]™¯áoÐa³¯P 0GK lÊ^±_¢õÓîk«²\O×ëBWw¬JI¯ÐõŠUP†tÅö+Žw>t}cU;½BוIÇñºƒÖ<{OŃVw8–uKŽ _Ý»{!½Âì{퀺ÃYJJ èÎ: vt1< o8k§Ä€æáì=hÍñ÷T•IÇñÚ±÷.±ƒÖ<{OŃVw8–ëÚ‰ÇHl+fÔþh•÷ÙZåK›Gò½§ÄXZw$_Jï=öª “Ø«²ìT×ÿƒƒ×!«rµ'©á¬-8õö\BÅ Zópì=ZÝáXJÈŠÆGÝPÙðï[·C}*“Žãu­y8öžŠ­îp,%>ê–¾ºw3öBz…Ù7öÚu‡³””Ð+œu*ì èbx@ßpÖN‰ÍÃÙ{К‡cï©xÐêÇRÌE Ñ ¹É(énÆ^H¯0ûÆ^; îp–’z…³Îèê]óXõž^¡«;V¥„k·D½´~âsèšÇª÷ô ]ݱ*eÄDýQ$péÖÀR–DýÉÂÿ@«¼û­±WeÙïÿ€‘í«UÞg£Û×Wƒ±Íãã“¿èɘV=†ûÖ«™­òŠô­'Â÷rœYÐÀ—ÿ¬L.åaö:VŽ(ƒ«õ̹p-ìfì…ô ³oìµêg))1 W8뜽ûñÛ¸7¬cå¸aCRÁïÊ„-+‡ß¿E”Ž•Ã…(FÿQ•·w3öBz…Ù7öÚñú†³vJ hÎÞ¡Œ©ýˆ½‚ï]™ýÔ¿Ò*ï³5ÛпÉÿვYŒÑ·òàX9èµ&ìÈK?›†¤b)3; IÿØ«J0W˜U%¬˜‘XUy˜Ù!©àX9è½aCRág*Žü+­òîÿ*kk•—=¡‹Æ°ɉepýÈ'~A\?2© ×ÂnÆ^H¯0ûÆ^; îp–’z…³Îٻǭ߰!©ð3Gþ•Vy÷•µµÊËþøÞ‰Øm\\ø^—·™ |ù/‡cÆ¡lïfì…ô ³oìµÃYˆÌô gí”Ð<œ½CSk{Ÿ½2ïÚ÷ºži!Õi}–êñxóÐï=½¯;ôK‰·þ×i /}(#Û[ v²þ)1›†¬bžoWbG!럱iÈ*vUxÄ&[…ÙQìýÓ+U¡ éÞ Œ©Ðçžu["ÕÜå‰ç7ì/Wk胕™cÑ%'Â Ç-  ®V#ÊÈöã÷ùN£Õÿá›3Šù>i|Tݾse¾¢c¯*Áà³Wø;‡.i´šYXdÑ-¾×¥ü­ÿF­òޤ܍}¯;ÝL ·þµ>Ë õnÅA²À›‡~ïéxÝ¡_JÈš‡kߨ{Ÿ×eÍã™ÞSñXVwÞ<ô{O¯ÀëýRFLÔÿÑE—n ,eIÔÿÑ‘,üµÊ»ß{U–ýñþÙ¾Zå}6ºEp}5Û<>Þàów2¦Uá¾õÄjfA«¼"}ë‰ð½Üg4ðå?µÉo0ÌÞTpìÑÊàú‘9·»îfì…ô ³oìµêg))1 W8뜽ûqÅ‘[ÿZåݳö¿Q«¼ì7j7lH*üãN¶jÕc¸o½2!qfA«¼"}ë• –|/§ß:öhû ’ ?SqäߨUÞ=kÿµÊËŽ.¹â!:§u\wàÍC¿÷ô ¼îÐ/%®nyÐ}êÞCVH‰Ø7dµƒXá°ÔŸš¤7ýÞÓ+ðºC¿Å‘pvK¶#lS¡‹©Š#™á”Áõ¦Ar“QÒÀ›‡~ïéxÝ¡_J\ÝërG Y!%bßÕŽ±ºCQJ*c½BQçî»QûÇ5æÞÃd¥«ôqÚnÆ^H¯0ûÆ^{¿|ãÂ÷rêéÌ‚¾Êàú‘ùÐÝu7c/¤W˜}c¯Pw8KI‰½ÂYçìÝߨÍ,,²èßËéòÖ£VyÇFR{´ø}¾seVDìU%|6U2”Áõ#ó“»ënÆ^H¯0ûÆ^; îp–’z…³ÎÙ»ÇÙoØT@—4ZÍ,,²èßK@Ö­ÿF­òޤ(ŽÜúoÔ*ïžµÿZåer }ïDìF ¾—ðøÌ‚¾üøüvÃÒ ?SqäߨUÞýߘµµÊ˾{!½b o°×Ž«à{9{’ä>ì…ôоÁ^; o8k§Ä€æáì}vÆñÂ>ì…ôоÁ^; îp–’z…³ÎÙ»ÿ%ßCƒõâ3t‘pšƒõ"J=›‡¬÷”ˆu‡¬tÓ£ ±wRÛL†„c_Rîɦ‚Ø-é^ÃNbóõž±î•‚n:ˆÝò°ô;L¯ÃTèb*jÉÈ;t Ï|[ :§Ar“QÒ0›ÇÞ{z…Yw쥄³[ºÎµ[Âzñyšu1ºHö#©U^‘?7,ïë‰ãŸÝgÁèöYMTÜý‹¼añü9­É ê†Õ`yþF*Þ¾,ˈ¤~R“w䘒°\<{O |Ü„.¤‚ºH8Íý؉ÀRž½'>ÎMsÐÅpX/’¡G÷Y0º}V„ø˜§G„¨ÖKrÃjÅ”$®zZ—oZ/É ´bJÖ‹ÏÐEÆ4] ‡õ"ÙïºéÌ—è‰Á,æv±¿“­üº'>#†ç[˜ä7Ö¸c«É› ÿq£Û¥øVt[Oá8BUë%¹aµbB~ÃâVè¶ž2‰Z/É ´bB>Þ×ư^|†.2¦¹Å±¿«^&?ЃYÌíb$`Üʯ{bpà3bxN×Â$¿aq7]«É5­˜|¿ü_²ø<Í-Žý]õ2ùžÈbnøuO OÄ𜮅I~ÃânºV“kZ1ù~ù¿dñyšƒ.†Ãz‘ =ºÏ‚Ñí³š ÄÇ<}? á¼ÖKrÃjÅ$®zú"ƒP¡õ’Ì@+&¹`½ø ]dLs‹c}ŒnŸÕDÅóg “ ùø«—ùKŸ§9èÂÚ_¾Vy±?uOÞƒ×!‹n Ì=-Çý­òŠü±Æ Á’§8gþ{´ÊËþK´.)Ç/Ñú„õ/Ñ` ™&ÔuE‚ß 5C —¼æ?zbU2#¯y_O ¢ btTª‰7,YÇûzbÈ¢^*§8%×Ï =1 ãVBÿ{uÈÌ­’­ãnÁ’§x† ]$œæ`½HÆ–”-©b †ƒ&­òbÿ¡^‡,¹¼žè°ÈV8$û^à™óYø7j}–J}ÅÁfA¿yð½§âA¿îàKQÉÜR%1µÊkÐ]™püw ³yì½§W˜uÇ^J\µ%¶ï]™Åèߨ Å]q•ó÷ºüʜϺÿZŸe†z·â*W˜Ícï=½Â¬;öRGÂÙ-¢BSÑG£G‚¨±¬y<Ó{*ËêŽgJAIFL ’›Œ’†Ù<öÞÓ+̺c/%®n‰=cùæá_ï)1–¯;ü+%Ž×¯Ùü"‹nñ½.]öþoÔ*ïØHÊ·?h°ÍƒÙü"û½.ͱŸPfóØ{O¯0뎽”¸jÏë'¼&ݶ£‰Z-%þMŒêÿ¿Q«’µß»_´!Æçdý?@ý×:”êÿïÓ`ŒÎ?®Ñí=¬ÌŠÐ€Uú¸‹³ùEö{]¢¢þg_ÜÛ´ƒ%‡ddœÿè‰!;fó‹ì÷ºôŒëýæÁ÷žŠÿo𥠃Œ©R¡dÜϺ'd´JÞ;›Í/²ßëzêÖÆ·ÎæÙïu%‡_³ùEö{ž¢þßúoÔ*ïØH :ÔÊÔkõ¯´êÙú/ÿÖÓdëÔ¿’eµ~ºÔ;y]ÂÎý¤&oþk”t”Ö‹ÏÓ¨‹dè·ñèQW\7ÆÒæ‘|ï)1–ÖÉ—ÄnIájÝÍßH…®y¬zO¯ÐÕ«RG~º#¦ð|¿.‚@ê¥z´Ã+B×/Ms .’¡›N½ÓªdöTÝ6¸¦Z/ ¨ BÓœV%³;vÞ:¨‹déý{Ù2çS5‘¨wƱÙü"û½ì Ð-ný7RÃh$]rr4P„¦¹…ÁÿýàuÈ’Ë;°'†¬ÊAÉõ3HO È,Ms> ÆËg5É]þÞHý¾inqì5^>˜äz­ðW¯…_²ø<Í-†ß°,χЗE šg×øzQ" Tß[ÿÇþ {ª‰°=ÇŠÄ_ìææ-2¦¹ÅpX/’¡sÞ¼í RªJo˜Ì_"xí×E]£cSÍþÞ&†zmüW0‰àµ_uAì÷ʧŠé÷6yo¢ÿ°^|^dLsÐű¿ØÁë{•s4æAŽÃ±u²ÉV˜j‘_]|žæ@] ‡õ"[R¶¤hˆÛ=÷žD¼YuÊÈ6¼B(D9Ð~dn˜  ®¹‚ùKTÊn.<¶[AB9è ÷í† æ/Q9(Cº¹ðHØ6n å û©ÿò*˜¿Då  éæÂ#aÛ¸$£n¿Í_ÝAJ›áÞSñPZw.%Ä{?Ðæ•󷞈ïjÜ óê¼þAJ›áÞSñPZw.%ÄëžÆÐ7y9±_ôÄMG]éÎé_ôÄEÅP’êg(#£¹€Œ GìÉìÁ=$…hì½§W Ôì¥ÄÕûA7ÿ<…ÝŒ½ð}KÑÀRÆöÄb y°÷ž^1Pw°—WÝóú] »{áûRÜXÊØž’Âÿ`”t”Ö‹Ï .Ns .†CÇ¢G÷ÁDfóØ{O¯0뎽”©«~ ³yì½§W˜uÇ^J槇k¯0›ÇÞ{z…Ywì¥d^áÚ-ÑD­ÏB?è7¾÷T<è×|)ñQ{^_¸~×ø¨*éûÖ­ÁPŸ@EãµÑD­Ÿø ]|u‘pš[$[ŒzË7ÿzO‰±|Ýá_)è¦GDò…yÿè 5’ïî–ÆÁèn7ŠÓEh$xóÐï=½~5ôKA7m«Â¼¾ïšü±êÝ7šäǼø–7ýÞÓëWC¿tƒÑ±*ÌëûòÉ«Þ}ï$?ð÷&ú° ´:ÍÁz‘ ¥È ó¦GÓá¯0ÏÊš¿é ÁyXÕ6|l²e o>ÿXX5ŸßnøØÄ0,¶Ãª¹¼¶ác“?÷V½>fÌõžø{ÃôK`½ø<ÍAÖÂz‘Ìc÷Ä òæí6¯8ê¿På¤Xò7T¥¢^œ 2œ…ä(FCÁÁï¡_P:6]à…Ä™„bÅŒ¹‚ƒ¨T*¸‘y©ºÐ­„þ÷ê™[%[‡õâ3t‘pšƒ.¬…õ"Ù?ôá¨_w†9ÚÞ­=1 ߇òBúX éÈk?Å3Ì[#±ãc¯œ4P23öh df˜½Å^bz…YEìeC™FÂ3±E6ÌÞb/1½Â¬"ö²¡¾×ëuë¿Qƒ19FRÌH¬"$eK™‘Ø[HJ ³†±×_z…Yë±Ê4ž‰-²aÖ0öúK¯0k=öb@}¯÷h·þ5“c$EÁŒÄZI1¤ÌH¬aHê/Ìêÿ© °^|†.Ns kCÇzòž†§÷ÿI9¾b}æhC ƒú]`èÂÊ|“tl·zÚó܉}CR;eî‰!)xÚótA$Ö’RRf$6Iïžö<Ÿ‰ IÑ”‰ýCÒÔÓžç“"±c!©…”‰UCÒyO{ê݉ݒRf$Ö*$µìiÏS5P .…Ãÿ$‡ïfì…ô ³oìµo¬w×Á?Éá3JH:ài!µ$«GHºMʌĒ*]!©œ<-ˆý‘Xö”Õ”‰%’b>BRžR¥‘Xd…¤ô¤ÌH,!©ÃƒKÉÀù›^Qäÿ$§‹äjéÿù¿Qƒ1R3I“¼aCÒe޵˜ §=Ol$ö I픹'†¤àiÏÓ5ëI))3›‡¤wO{žò#±b!)š2#±HšzÚó”‰ I-¤ÌH¬’Î{ÚS¯ "±[CÒCÊŒÄZ…¤–=íy.G•ðàR¸Á?Éá»{!½Âì{íëÝuðOrøìI<-¤#±z„¤Û¤ÌHì0!©ÒY’ÊÉÓ‚˜‰eIYM™‘X‚!)æ34$%ìi!õ.‹¬”ž”‰% $uxp)üM¯(rƒ’3#¹ZúþoÔ`ŒÔ BÒ$oØtYH:,.|¯|›™˜¾ü_âÃ8¿£> s´%Ÿ ¥µ µœŠ‡Òn Â=<]sëPZ« \Ë©x(íÖ ÜÃÓ5gÞ°´ã×`¯<ß·¶C}BrL ­<7l -iYÉæÿF ƲYûߨÁX6{ÿ7j0–ý7Ý>˜èã5PéËIY}’ªßÙEqöBzÅ@ß`¯=Õu„×@¥/§å°*_ÕïìËÙ‚½^1Ð7ØkOuÝýK|çwÔ‡a޶á“ æLAäÓfà7j©N kÅ‘?yE—:aó‚r»op ›êP7jÿÆùõa˜£-Aø¤‚ù¯G2· ¢ f°¿6p`NÕ jRg’ŽÏÚ‘X„;ýà ©xÐï|í§ë ÝN?øB*ôû_ûé:ßé_HŃ~ßàk?]g¬oåIz®ð$=׃ìôƒ/¤âA¿o𵟮9ºø¼ ´H8Íñ=š“M NçÎáƒSÏÀ?ž ™=ö' þINý@öÙ–™Èü æàOÈàB†îã KaÂZô‡ÜoØÑìr_‹þûov@4»Ü×^Û~ÍvÀ‹}Ûd¯‘m™9€ìiî†uì13pì1¿üƒ_èÃ0GC¼•¬ò$=׉ÖLŒ¦¹_þAŽ/ôa˜£!ÞJVy’žk̉ÖLŒ¦¹_þAŽ/ôa˜£!ÞJVy’žkö_¢5£iîàõ½ú0Ìјäò=טOs¯ïÕ‡aŽÆ$—ÿè¹fŸæ@] ‡õ"Ùóné¹÷Ä0=vtt(#[„¿E<í‰õ_‘Àå«¥,©¼ôQ$ÿ¯CÊÈvý‹žˆß°»ÊڵǪ'BcÕ$Y®§øã3I!VŘåzŠ?ÎÝDC\×µ'B¡Œl Ìÿed{í—Ï^q÷Á  ®×~¼¾W†9Sð ¨áì3)1 …³auYŽ™|@\¯ý:x}¯> s4¦à…ŠêP¸ö±ôH<ªáÚgÌÿ¸_¿ˆ[áàõ½ú0Ìј‚”‘­úÝ>˜Xá×m»ÏÊRÜ}ۮij²¬Ïñ7üF·&[ñë†UâYYTÜ}Ã*ñ¬,_¿üPWu‡pRb@ßpÖvQeYS½þíÆüÿ˜¼.áÇxeó€NíÄö_g†9ÚaÛ¸U‰í{WxEüЩ£#£xØöëìÃ0G;"lIJBwÈö!èÔÑáÑN“êP8û˜g¾í ¢n_ÎÊ“œ=!W“ôÁD©‚ù?¡Œl¯ýÒÕA½vñÃf_¡Ã-°Y|TÉ1À±ÖfÌÿ¸_¯ïÕ‡aŽÆ¼ê_8ûLJ ¨Cáìcžù¶0ˆºÝ*œ•'9›.L„¸š¤VÜ«TÁü‰ûuðú^}æhLÁ+ þ…³Ï¤Ä€:Î>æ™o냨+ü]ÇÕ$}°2ß­ æÿ@H„2²Ýº_¯ïÕ‡aŽÆ¼ Œl?ö7„B*oßj?]gpP×­û¥Uâedtë.~Øì+ôa˜£%6‹Ê#9æÁ¾ÊepýÈ|õãQû:> éë¾ñQûi9”‘Ñ}…ôŠußø¨ý´epݺ_¯ïÕ‡aŽÆ¼vg!%ô gm(cj/ôù~I\îÇ¡ ®.!äêx—vÈU„¨ˆ}…ôŠußø¨ý´:(#£û:> éë¾ñQûi3ð}…ôŠußø¨ý´k(ƒëÖýº5}^ß«Ã)xAÙþ¿ÏèöÁÄ ¿nÛ•xV–âîÛv%ž•eýxŽ¿Éà÷Ý>˜lůV‰geQq÷ «Ä³² <~ýrá¯K€2¸þ;„³ú†³¶‹*Ëšêõo7æ¿oò‡ád@‘à¨ð (cx¯]ü°ÙWèÃ0GK l•GrLø*”ÁõÚ/­/ó $î×Áë{õa˜£1¯pVžälãZ‘à¨ð‰]ü°ÙWèÃ0GK l•Gr̃7|•GÁü‰ûuðú^}æhLÁ+œ•'9{bý=ù¡ŒŒî’.@5Ü?$ËÙ}¸7ó $î×Áë{õa˜£1¯¸š¤&Ïü¢'ÖÌÿ¸_ˆ edÔp_'Î$+æN‚(˜ÿ!q¿4 ÊȨáïº@eÿ/Ø õ ¨Lz?.üÖ¼+KŒ Œ„E8# ¤}oØ@ZÊڵǪ'Bwý@Ú|×—PVfårgXá±ëK¸‰‹»þ3dTäzíµ°ï>ÛÈþobÌ­ÉÀÁë{õa˜£1¯¸ji ÷ýàõ½ú0Ìј‚W&„k-ý?ßw ÆHÅ^ðÙ+îí¿‰Ñm ¬L8öàõ½ú0Ìј‚W\µ´V$:Ö`‹¡ ©]{¬z"tp×—P&—‹8ËÀJ×£ïúneVþ®¿")ù…æû£p-ì»Ï6ü71æÖÀd°ƒ×÷êÃ0Gc ^ᬥ†û~ðú^}æhLÁëÆ:\kéÿù¾k0F*ö‚Ï^q@ÿßÄè¶V&¬^ß«Ã)xÅUKû`ÅÁ²Ø ¯ïÕ‡aŽÆ¼2Á±¢°†2¤ví±ê‰ÐÁ]_BY™•ËEœe`…WÄ®/á&ù®ïÊàŠð }÷GáZØwŸmdÿ71æÖÀdààõ½ú0Ìј‚W\µ´Œ†û~ðú^}æhLÁ+µ–þŸï»c¤b/øìW:ÿ&F·5°2‹ñÁë{õa˜£1¯pÖÒ+®Ïñ€2¸"üBa®…}÷ÙF‚ÿ&ÆÜ˜ vðú^}æhLÁ+œµ´Ñpß^ß«Ã)xÝX‡k-ý?ßw ÆHÅ^ðÙFÂÿ&ÆÜ˜(=x}¯> s4¦àÎZÚ¸Ž½pðú^}æhLÁëÆ:\kéÿyì ÆHݰßC·„õâ3¨‹„Ó¨‹á°^$ûb^Á“÷Ä0ü§¾gÙaÛ¯³Ã툰”1µï>ŠB*c}CQ{ì_õø]pØöëìÃ0G;"l*÷»Àè¦ +üºQSâYYŠ»oÔ”xV–õãwü5þF·&VøuW‰geQq÷m\%ž•eàñ•”d‡pRb@ßpÖvQeYS½~jÆüwòn~*û;yGºk|,CŒJ„\’P×w‡pRb@ßpÖÞ弓}`û÷ƒ×÷êÃ0Gc s™6®þûÁë{õa˜£1…¹Ì7\;ÞZøÙ¿‡ °^|u‘pšƒõ"™çí‰a¸ÇÝs=È?´Áðí`É÷ïÃ0G[RÀ8ð.# éX¾yø×{JŒåëÿJqQM8`»5æJŘë2ÇfÒÙñÐ/¤Wà}C¿¶‹eY¯ðõo4º}`põFM‰ge)î¾QSâYYÖßj+³r…~—M w<ô éxßЯ½;“s¹»Î tÙŽ‡~!½ïúµ],Ëz;Á]™‚ù¯Èå½ï¹–ó°+ù_ùlªù »Kþ¯ïÕ‡aŽÆHÂY*ºB¸–Џî?ÚÁ’ï߇a޶¤€ñ JEƒ!t>Ù•)˜ÿŠ\Þûžk9o»’ÿ•Ϧšß°»äßxðú^}æhL$œ¥¢+„k©¨Á?þF nØxP*ƃ.ûW°^|žæ~¹›}…> s´ž±ž†ïSÂ@!%Æ”¾a öš‹*‹ùøý©šaƺãI`òR»ícËBúX ì[Öf¹?Å7§9èb8¬É!ˆØé†Ù[ì%¦W˜UÄ^¶x›‡Ð{*oÝ!”â} Æüß4þ ©N«(ÐãƒÉø0K0öbž^a–=ö²Š>ú^b¯œ4P²u|ë¿1+B·ÿg’ŽŸÙ!)BX/>CÓ¨‹á°^$ûåäøÞû0ÌÑ”y,÷Ä0<’ïxã.‘Ó½ƒîkÌ k?ÓÍWf™øçÐ5Uï麺cUÊî4n ]óXõž^¡«;V¥ÜXï®ûtlª>Ó}cSµŸâjL7Mõž>ÓuǦJyŠsÅ~ 5‚¸ø/ÑzRÇÇÿ ÞâŠ_¢AÎãc¿Dƒ<.|¯Ñb&¦/‡õâ3¨‹„ÓÜÂZX/’yòž†ÿC ÿ“r|Åú0ÌцˆÍCÖ{J ĺCVÊΖµ#±CÉEâÖH¬1{ÄŽÿE&>X™tB¿V\é½Q Ä Y1O‰Xö•ÕïU!öÊI%[ÿ7j0Fx…÷ø¿ñàõ½ú0ÌјÂ\@ÿp6M‰ gÑ-áȸ5è7¾÷T<è×|)O×\wúÁRñ ß7øÚOתóÂnáY&ÂÑäúEþ©ÑÇ•j0/ܰûñ ß<øÞSñ _wð¥<]sÍ2ÿ¸}¸ Ááí¹óÞ~jÆñA¿yð½§âA¿îàKyºæªÁ¼·cV„n¬Ÿ §¹…µ°^$óÜ{bžº‹–|ÿ> s´%KB¿:­óÖ}ïj "ÿ$Æùõa˜£-Aç÷ú·gÅB¿:­‚·YE -[Ê ‘½Òï-ôKL¯À«ý²4gï)1 îp–‚b½{‰éU{ÙP¦qò{3"ŒAõA<è–¿A‡Í¾B†9Za3ÏX[yž®3Ö¸õ6tËß Ãf_¡Ã-°™g¬­ s´%KnØxЇðXµ•ç)έa½ø¼H8Í-ŽýbÞßsï‰aø­‘í(²u¦îÈöŠl…?‰‡q~G}æhKÆ©ú‰?9£›.Lnøÿ႘Õ9øÆ >\cVçàØ õ—¼¾W†9Sð ¨U8k9%tk8{pQeÑQ}…ßÛ„ô/9x}¯> s4¦àÐ?œMSb@ÅÂYt¿Å¬Bë|æšAÌzáBÂÝèžAÌ: r/ãäŽVÈù—¼¾W†9Sð gåañªúÝÒ‡êäŽVp¿äàõ½ú0Ìј‚W8+‹Tסß-}·;þÞ&+˜¡ß-Ñ-F„GA®°Ç—ý’ƒ×÷êÃ0Gc ^qõ!|×ö-o“_rðú^}æhLÁ *Ë¢ Bчð¨m"Ä·ÂîúK^ß«Ã)x…³ò°Ø®4¾⪾¡«‘t(m„{OÅCiÝA¸”§k®Yû—hàöݰ1ë_ý6û }æh „Í ²áž±¶ò<]g¬¡2.œ•‡Å?ªV¡ß-}·üÞ&±º%nø{›Ü~ÉÁë{õa˜£1¯pö!\!\ûAîÈ8Dƒ1Í8ù->Xòýû0ÌÑ–, ¼·Ð/1½¯"ôË†Šø^©ÁY1t`ïR­—DÁŒ¹*âAÙRfÌõJü%YÀÅ4˜Ï2q·óâY¸}ßCÿÅçiÖ‹d_Ìkå¹÷Ä0ü—ÿµ¡¿ÅK¾†9Ú’‚%±|oá_‰)1–¯"ü+[Œ5Eï©xŒÕŠR Œáýcùæá_ï)1–¯;ü+eßx—‘À„°ÖÙÃ>˜(Ý0ü’ƒ×÷êÃ0Gc ^¨ˆ °Ã¢ƒÄÕåãxq”$Zü—hàöý­Ç¸üwɃ• „ÿ'¯LØ&¡Û¸õ—¼¾W†9Sð gI¯LPÜôø%¯ïÕ‡aŽÆ¼ÂYyXüÛnõK^ß«Ã)xô!pÊàútË_rðú^}æhLÁ+®’þUË*m_ÏRñXÖ7ž©ýt¡þ­³‡ÿäÛ~}æhG„m³Ï»ü]q=þK^ß«Ã)xeB¸ÿŸÿ Ü>ÜóK^ß«Ã)x…³ò°ø·ý¦Á¼œ B(iÚö(É~íÇñµór†22úCeSGÂYÒ#‚r»8T&½?{_ÏRñXÖ7ž©ýt¡þ’ƒ×÷êÃ0Gc ^á,iW×’þõ0’ÿ'O¤îËâ™B*ËúÆ3µŸ®3Ô—\FűŽ_rðú^}æhLÁëB¸vX|l(0æÇ"‰¿ÇŠø%Zgÿ›ü’ƒ×÷êÃ0Gc ^A½Ëÿ¢·_rðú^}æhLÁ+¨OM ܾï¡V°^|žæþ¡Gýæ}æhÓ«ž†ÿ5xúïãVs&0÷´ÜÀ Jz–ÇƒŽŸæþ ”‡ßRãüŽú0ÌÑ–à±ÐÃp ÔOÄ0̓ÕÞSb Sw°ZÊ~û™‰V&P’ž¸^+>‡~·<èØtÁjI–úƒÉ-”6½§â¡´î \ÊÓ5WÜ[°—tàÝÒð¢ºuW„ ©x(í„k?]s8¾}–ÒŽß•áB*JûáÚOלnW„ ©x(í„k?]gà¡´yî=¥uáRž®3Uƒ½óé öZP VK¥w$BPn¿vß®4RñPÚ7×~ºÎPƒ½¤ã±niø`¥«ˆ¥uöð¿IBd—az VKL‰1LÁjÙ¾úéY±`/Më%ùW¯ïÕ‡aŽÆT!—¤çú'—§Ûs¦GÈåé¹Èåéö\~ÑÆõ®4RñPÚ7×~ºÎPgH‹ÿÌE-‚½ò°°oÿÌ™<‚ȧÍÀÇ‚½¤±HKw+$þ*ï÷`\J›áÞSñPZw.åéšë¿Ò:{8¨Ðå¬Ñú®4RñPÚ7×~ºÎPƒ ůEíæ@] ‡õ"[R¶¤ž{O Ãùÿè¦ÇOð·ø`É÷ïÃ0G[R°$ü«<,þm· ÿ:žî;ùG2å¶xªfàÖBèWN(™ùD˜à¿A‡Í¾B†9Za3 ä¥$u(|},eIý _Ÿ ñŽ'õN¬~ÉÁë{õa˜£1¯ëp-+O„ø/9x}¯> s4¦àÎÊ“\"]s…é_rðú^}æhLÁ+®Ž÷ÝWˆý’ƒ×÷êÃ0Gc ^qu¼Ï^q=ö—¼¾W†9SðŠ«ã}ùŠë“~ÉÁë{õa˜£1¯pVÿVºJƒ1mÅuпA‡Í¾B†9Za3(cx󸍕IÇñÊepÍz g‰)1 Šp–-Õz¸Cz$Õ0\ë}WÆñ®¡ ®9ú(®"Ç;Êàš+ŽÄ£*µlé‘xÔ[¸–WÇûÞ• ù/9x}¯> s4¦àβâ áZVÁéí—¼¾W†9Sð gåaño…Wοäàõ½ú0Ìј‚W\}e¥+Ò_¢ÁhõcW\­ ý" s4¦àuc®%},^‚¡_ÌÓ+ð²‡~YE}¯ ¡_9i dëøôoÐa³¯Ð‡aŽ–@ØÌ3ÖVž§ë <+â—hàöáJ=&ƒý’ƒ×÷êÃ0Gc ^qu¼ïn¼ý6û }æh „Íâ£òH0„¯òh0¦ir‡m¿Î> s´#¶PÆÔæ¡ö·Ó+Ô*âV6TÄ÷š÷—C ýê´^(ƒk®8ªײ¥GâQoáZâ/9x}¯> s4¦àed›£"B¼H6; ÷¨muÉ~i·þc'o“5”Á5gåI.q2úExð:dG2· [öOû½­Ìêý%¯ïÕ‡aŽÆ¼ÂÙa'ÓŒ‰0æŒYÍ tüÌŽE¸/‹g ©x,ëÏÔ~ºÎÐíËâ™B*ËúÆ3µŸ®3ð}Y¶ýší€û¶Ó¨‹á°^$óØ=׃xî=× B†¹k;»¡dÅuó æ/!—ß²çZÎ?•9¾b}æhCy¶öÄ0|7c/¤W˜}c¯}ÛîbYÌÇó§Í0÷|6Õ÷†Ý7Æ^*úP]ß°!)CÒeY‘Xü Û·³ï:ÃË8€‹Ç^*?þZgû`b=ö.’âÿƒ0‹kàöíì»Îð2æüãwúÁRñ ß7øÚ,ùÓõã&¬ŸA]$œæ`½Hæq÷Ä0|7c/¤W˜}c¯}íbYÌÇÿ§Í0ùžk+LÜ7¬V—î]Y, )‚PÖ7µYþ§ºÇMX/>Os°^$ó¸{®õw3öBz…Ù7öÚ7Ñ.–Å|üÚ 3öŽ÷ÁÄ=­.Ý»²XR¡¬o,j³üOu›°^|žænXü¬É~ùÌÏ…Ùñ$0ɵºtïCy!},†ô äµYîOñÇMX/>Os7,þκëE²èÿIÌÏ…¬ãéÚ¬ätï–þ龯gèÖ¨]À=1¤y ï=},†ÔÈKyŠsÕ@•ëߨª‹½ã}wã¦ö!¼>CúòÚOq×@• _¦*W iÈ{O‹!uòRžâ\w3öBz…Ù7öÚ»39—»ë¿QT‡ ß˶31 |ù¿1+B·oÈ éc1¤o ¯ÍrŠ?nÂzñºÈ˜æ  ƒÿ¡³”ßF7]˜Xá×µÏÊRÜ}c­Ä³²¬wâo&üãÝÞÃÄ ¿nã*ñ¬,*ij² <žâ¯òð³žäYÀ·þãNòì‹‹Ñ…Ižµq·÷0y³ùA„¦9PÉØ¤ür6ûæ}æhÉ8DO ÃÉ8¿Üž¬Mq‰ž’‘q~¹=1üT—Áýtnº0¹áBæëiÏ“ ø•yyÚó¤‚Ö£ê÷Ý>˜ü¸{ðgÍPñ|ðgÍø†È²¸[ƱédY79ö÷Ý>˜ü¸{ðgåT<üY¹ßgtû`rÃ…Ì×Óž'ð+óò´çI­I³‡Í¾B†9ZòšGØÃpòšß²'kÓÇÈkaO ÉÈk~Ëž ~ŠsÜH ¨ BÓ\$VIG)3{…¤Î~ð…T<è÷ ¾öÓu†9ÍýK|çwÔ‡a޶á“û@°Ò+ú{íLØ]cª?Í…Ù?ö¦éfÅb/º;÷!¼>CúòÚOñ ušÛ‚½^1Ð7ØkO}´?ŠæÁÞ{zÅ@ÝÁ^Êm~4[Os1LoÁj‰)1†©"X-[Œ5Eï©xŒÕŠRPŒ§¹ãÁë{õa˜£1…¹ÂYH‰}ÃY{šû%¯ïÕ‡aŽÆ¼ú‡³iJ ¨X8‹î·PÚ<÷žŠ‡Òºƒp)O×\É4÷o s4¦0Ð?œMSb@ÅÂYt¿íôƒ/¤âA¿o𵟮3Ôiî—¼¾W†9Sð ¨ÎÎ§Ä€Ž…³¦¹ƒ_±> s´¡<[{b¾#†¬±oÈjߨ¹¨²˜ßŸªæ4fo±—˜^aV{Ùš‡³÷”Pw8K™æ~ÉÁë{õa˜£1¯€æáì=%ÔÎRnØýø4wðú^}æhLa. Vá¬å”ЭáìÁE•E7Íý’ƒ×÷êÃ0Gc ^½…³Ä”PE8ˆ™æ~ÉÁë{õa˜£1¯€æáì=%ÔÎRöÛ4çØq1sì¸Éû¤Ü±OÚîØ'¹Ð±OJæXQüp¬(îÿ³SêÔ¦Óý'Ñ*x›æÐ$¤"¨mO?PQƒë]ë%™æ¨mãØyë°^$KïßëÝp¦<©ŽÚ PïŒc3yÙïõžè·þ©Ñ;’‚.99¬Ÿ§9èâØ_ìàõ½ê†9ó ÇáX:Ùä+L%õK ‹ÏÓta-[R¶¤»çš®'oï$PîY÷j—=L¤†Î}Õæ‡,T–U0QŠ¡ðÁй¯ÚüÅ@ÝxCç¾jóCvÄa nP!q¿:çýÛáÕ`¨MFŸ¡2õÉC¡2NVçÝ(ÌüvG:ƒ@Z%%….NsÐ…µlR<îžkº;rk¨M¶jð‰'ýX£ØÌowIì…CRøöi5j“}÷Q›lóíÓj0Ô&hV ú‡öQ3Þv“„š17dNÄv‹îä0 VÌH,|û´ µÉH t‘pšƒ.†{ÚsMÊî¥HàòÕÀR–„.2¦9èâØ_>ûc<žkß­0ŠAe¦bmßa•òì=*ËXãüGϵÏFÁ÷ݾ|²¿ˆÕ`¨Mp½Z7¥± µ ®×«O|¯Âì®’­¤Àzñyšƒ. ^ë± Cmâü†Õ`¨b5 ŸQã}=×toXÞ×sMJ “o¡4xZ =¦«ãµºýþ-Á€ÖgH´>ƒ‰8þÇNÞ'ÕáãßB he}} E0 ÁF:ßB | E0ð-Á€Û•ûŠ`à[(‚h°<WïZ )÷-ÁÀ 4Ðcà[(‚o¡f´Š¢\ßB ̬´Š¢\ßB | E0 u ño¡´²‰øŠ`@↾…"øŠ`@Ÿô-Á€Vo^ßB hàwæ·PZõ õ-ÁÀ·PßB hµ|ôo¡¾…"øŠ`@ë|‚o¡4°àõ-ÁÀ·PßB h°…·o¡4 £o¡¾Ð`™ú-ÁÀ·P‹ÏСiÎsï‰ðq¢7 ¶"…ú©Å$ž5)R¤Ho¿·‰Õ±ð½„!7¬¾œX †Ú×K¬Cm¢’± Å&Ûð=aQ·ñ=aQ·¯ùž‡˜¼£i–Ÿgï‰pÈãÜ/‘ؾwBè‰p‹ºÝŠè‰p‹º}Ê„ ?Ðáu»?Ðáuûx~ 'Â!,æöÛ 4ØFAÜîƒÉ;šfùyöž‡;ãAïñ nâOsÐű(u—Âbø4pIù±+ò›hÝsmM Ôì½Ç0¥«½ˆjú«–ïã.º8ÀÒÇ‘'Gù·ð–ÅêÈoˆ}CV;%bóõéˆ}CV;%bóõ΋÷\Ó‰g׾!«±yÈzÂÍãÙͱoÈj§Ä@l²ÞƒpíxvïØ7dµSb 6YïA¸÷xvÝØ7dµSb 6YïA@Ýñi¥bßÕN‰ØPÉ<÷žkkBWw¬zOóH¾ùTA7OTÓ?t¥Äª×H^¥ÄÆ^üºçz üGϵßÓN„ïu—žMÑÀ—û{l™!¿î¹^ÿÑsíwsT~ çÚ•êKm~Ýs½þ£çÚïþpª95 Wð=×Ö°t»êñïUavÓ@ÉÖA]šæxöžkk8•ÿ蹦3¯?¯/¡øìÉõí4ØæÿÑsí½Cê·Pž¤çzx_Ï5Fްöñ=×ÞK(™ ¸ÊU'>ó´çZnv°:²?â?z®½wh<ÿÑsMGBù^ÅåP®ÞðYÊäIz®Ç÷õ\ctÃj0ž½çÚnÍô\Ó‘P|`ü}¶‘´ï5Ž÷ßfZÉî/|Û\ëW¬Ï¾WWg¹VBÊy `}ö½ú`vÓ@ÉÖA]$óä…åù²༞kkµ0Cœ7Ðsí}¸ö! Î蹦®µ=÷žkkfÈB€GòµÃµù YðH¾y¸ö>C<’ï=\ëž! ꎡK™! ÎOé¹Þ@Ï5Fñ¨W¸Öùm‘¥Á2Ô¡iÔÅpI~|Ïõ8ðâ=×í› ~ÖÆñü@ϵ5,kª7~ çÚ•ªqž¤çzxº=×Íæi_þ£çšÎ¼þ¼¾„¢ê«úüºçÚûDŠÕHkÏÎ@ÚÔEÂiîÇNÆóì=×Þgpü@ϵ5,ÝNpvÓ@ÉLž½çÚšŒâãùžkkXJÕÐ âß«Â즒­óì=×ÖpÕ`í›æÇzî=×H5P:è_þ;J"sŒÀœÉßôR1£3&ÕûüM¯Ò “¿éUš¡ãoz•råoz•fà¿ÅK¾0G[R°ä†u£[ŒŽŒà©ša¢[ŒŽŒà©š¡C·ÁS5G·ÁS5c`„Ù(Šg˜r+”þ,ùþ=ÀmIÁO{ê]Ç\ßxP;eîsñ °ãéxßЯ}c­RÒüMï@÷ퟀgêEÈcõ"Áßô–[ó7½Ç_z×üM¯Ò “¿éUš¡ãoz•råoz•fàüMïøŒò7½ã3Ì–Çê]ó7½¤3Lþ¦wŽËZˆ¹^( cMlÑQ<¡z$¶¨3žP!k9æšz 0Œßc‹ªñ„ΧGb‹ŠÅŠj°<WíxЭ«_rðú^=À)xežáÚ­9¾íæh¦àînEF0æÈŒ&·ß Ãf_¡˜£%6)7zŸ®¹|ëoÐa³¯ÐÌÑ›”½O׿D=ÔLÐ@)é×f=žÐ¶Ÿ§9 ”ú'ðàõ½z€9“\~JÏ5Rry=×¢L‰äòSz®7D.o çúÉèók ”’ÎÂTƒ‡˜ãoz•fôG:çoz28þÒ»>Œó;êæhKð@Ï5RAÔÙÇ»utdOÕŒiî†u„£ Ls¿­ÒA¼¾W0GczÔžk*°ØOûøÀ4§ÒAÿ¼¾W0Gcz ç©ð(nÍ'×T¥ìø5:2‚§*‡Os‹d ƒ5P:èèƒ×÷êæhLÝsT@2Ø›OHïÛ„Ã…ô±Ø¦o8\{äÝSœ[{ï,ìÛÇþ(eÇTäèù¨OËé„R§9 ”úàõ½z€9Ó=×H…GûX( ©xŒõ E푈§ëŒõèÈžªºiº0xq¬çÞsT¥ƒþåk ‡zc­RÒžæÒ÷W«à õnÅÁuãÛ`Žf ^q5÷ÝWfŸtãÛ`Žf ^èñ¨VáZËé‘xÔ±p­…LøŸÿ H»xóÐï=½¯;ôK‰«¾aǯ|Ó³»¾<ðæ¡ß{z^wè—’ù®8Èñµ{€9šƒÂ8gHKÙ‚½^1Ð7ØkÇòÍÿÞSb,_wøWJû†k!Ø{gaßîþ%(e?ÈñÍ{€9ÚÁ’Àë ý éxG¡ß}ô½:$ô{i d븺…§¹ßïÕzI²ç/Ñà!ær|Åz€9ÚP‚»{!½Âì{íLÐ@)éÅgè‚Ð4·°Ö‹dlIÿGÿè·È Ó¯˜¬oÞxUï ÷¸›ØßÛäéˆØ´“w´J™?uü½yûýŽúM{€9ÚQ=–{®‘Æ7ßiáXHŃÖ7kÿOˆ¾PÙðïÛWƒ¡>•IÇñ¾‚Ø®‹U!½B×7Vµãê;ö@b>0^tÎíùïmb “ªð½îfw |ù()¬Ÿ §9PÃa½Hæ¹÷\#ý壖äøB0GC¶ØÇBQHÅc¬o(jé¾ ·®8÷þK^ß«˜£1…¹p67Ò}Wl§{_qïÑ銳ºùʼã[›àŠë1™ïÿü—h@ÚŃß¼˜£ ,ÙñÐ/¤Wà}C¿öîýÞYØ·»‰JÙÿ݇h|抻Óã[f‹–|ÿ`޶¤`É êF·ÁS5Ãü^²w­‚·˜«U<¨å”s‹µxÇÒ+ðZ…~-£Lãßâƒ%ß¿˜£-)XxÇB¿Ò+ðZ…~-4gï)1 îp–rÃÆñ [ŒŽŒà©šÁÅòu†Rb,ßQø×ÿ{uHè÷Ò@ÉÖa¹^Yp„-‡gç~‰1÷K^ß«˜£1¯ÂYH‰}ÃY{¿9Âr½\q¥÷ Ç×îæh ŸÜ‚½^1Ð7Økïî`ï…}»û—h ”ýß}ônŸ9ÉÀ׿ÅK¾0G[R°ä†u0åOqŽs„åzý’ƒ×÷êæhLÁ+ cᬅ”P«pÖ2zdÅÙ–ü,ùþ=ÀmIÁ’À;úµ^×*ôkñ½ÜŸt¿Ö¿A‡Í¾B0GK l6Rnô>]g˜Ž°eö—¼¾W0Gc sáìE¶û“xçwÔÌÑ– |2ê ö é{ôÑ÷ê`勒­gÂÿü—h@ÚÅ} Ø é}ƒ½v°Zw¸ö`/¤W ô öÚ»3Ø{ga_áöœ#,÷¡¿DƒÁÖŽ°\¯„Ër|í`Žæ ðÉ} Ø é}ƒ½öîöÞYØ·»‰JÙÿÝg®Ìw³_¢ÒAi §-a¹Ö³s¿Dƒ‡˜û->Xòý{€9Ú’‚%;ú…ô ¼oè×Þ¡ß; û · ;ÂršàŠk‚ƒ_»˜£9(|röBzÅ@ß`¯½»ƒ½wöíî_¢Rö÷™+ó5ü%¯ïÕÌј‚W@ÇÂY )1 Vá¬eôÈŠ{þ,ùþ=ÀmIÁ’À;úµ^×*ôkñ½.éìä†ÕzI̘«U<¨å”s‹µà˵þK^ß«˜£1…¹pöÎ"Ûî¾Õ¶~ãÛ`Žf s;„³ú†³ö¾õß}ûÊäãüŽz€9Ú„O¢›Ž{-¤W Ô*Øk9 y8{O‰u‡³4 AìÀa)ÛR”$®R:êc›ú%¯ïÕÌј‚w(½;ú…ô ¼oè×V ÿJ!)$a' , $áì…Õíî_¢Rö_rðú^=À)x…³Á®¥8Âr½~ÉÁë{õs4¦à]ͺëE2Ͻçiêþ->Xòý{€9Ú’‚%ûòá_!%Æò}ÿڨr7Ö(‰JI‡~/ ”ÌŒAÕ5·æ>~‰J¥œ¶ü%¯ïÕÌј‚W@ÇÂY )1 Vá¬eôˆqûoñÁ’ïßÌÑ–, ¼c¡_ éx­B¿–QßK°ÊÞÑAnX­—DÁŒ¹ZŃZN™1×±xP ¿äàõ½z€9Sð gï,²íì7Ö·þ H»¨RÑ»óÆzw ýÞYØ·»‰JÙƒ›}…`Ž–@Øl¤Üè}ºÎ0‹–|ÿ`޶¤`‰§Â6wÐ*xÓ@©ÅFëw,ôk!½¯Uè×2úÈSÁá{µ^’À;úµ^×*ôkeN\ø[|°äû÷s´%K<ž¹¿Zo(µØh=ðŽ…~-¤WൠýZFy*ÐîÕzIïXè×Bz^«Ð¯e”9qáõ­¿DÒ.j TôµJI^±Ð/š^W ýΣˆØÐoª’­ÃzñÔEÂiîÛ-ê1êb¸Çî¹Fzcì½³¬¶ÏTäþ†PHÅãíB푈§kn=z>êÓfè~Ö“+Ø{gÑß¾5Ø{gaßžß°Ô¢"GÏG}Ú |öBzÅ@ß`¯ ÔÜ .NsžuO½ëÉÅâäoz•>>0ͱ%=x}¯`ŽÆô@Ï5RáѮƭ^¡ö[mϺ§Þõäá¸u«O0^¾‘qK߉Oø›Þ£pæ·½Ù¾¸Ààú²éÙ¾ ®ï$Ëßô%ÃÄ­ó7½Gá¸o{ɲ¸[ƱédY79–¿é=J7J:ÍùàFê÷Ms.á÷Ms¿ÅK¾0G[R°dÇC¿^÷ ýÚÓ\ÌÕ*ÔrÊŒ¹ŽÅƒZ¼céx­B¿–Qæ4wðú^=À)xíÎBJ èÎÚèóiÔÅpX/’ýò5 xÌOýàuÈ’Ëô\å#—…×€}SÙ÷ß5`ßÔ­¿D‚Jü²Ã¶_g˜£ñÌ÷\{írV0ãyí8Þ¼jÇÆæ)3¼ ±±oè éºÚ±jŽ2JÑ“\ÂJÑ©£mÿ¹__Ìs4;òÒ*Ê·B§Ž¶ýç~} 0ÏÑìÈK« Ÿßúë˲LðcyöìNþºÖ‹ÏÓ¨‹á‹cÇþÔ']Á(нÇèþÒøÖhEâèZ‘0q«hEbózWÌ@+TjE"~ùøÃô½Àâ™S_þï |N&îס¹ø¼pÔEÂinaðâØ_¾ù©ÿ &ðÁäú%ÙW‚Jü{4`ßÔâó‚Ð4]ë¹÷\{ýÔ5 xÌ/ÿàõ½*À)xiÀ¾Iè€+ÝhÀ¾1œ}çý;,Mèœþ :lö*À-°™웾óúÝH—B¬ûÆGmãï[·–2†nÏûÆñÚÿ®Á¾×Hzæ\d5`×û½^3ûþ Ò;’òï aýÇ41Ô€°R|ë̹ÈjÀ®÷{½ oý%¤w$åÖ§6äß5 ˜àÇN²ÐÅçiîZ‚Çüd:S^ß«ÌјäòSz®·%—7Ðs½YJ$—ŸÒs=Nry=×–|¯×"·þ+j¤Üúo¤&ü©‰ÿØÉðiîQó¼¿Å̱MræÁë%—bϵ¼ÿƒ·,×ãœÝiŽÚêàuÈ’Ëô\Ë;ÍýŸ–R§F:Ý‘ZoÓta0ºéIjc);ºmpuj½$Сiî^ß«6Ìј‡ÍÃä5/^˜ŠÑï›æ|p#õû¦¹E2¶¤ž{ϵ×Oàÿ£G§¶ÉïÝžÄÿ ‡m¿Î 0G;"lþ׎䚧GÂÿB$×÷Ç 1†‰‹aþãéfíØ›ÇÕ»ï ù¼¾W˜£1/¨,«û&a oröai vÌÿc’³­Ž„ÿÓ€}“0ÐwÞ¿ÃÒ0;{-|ߺ5°”±HìXHj*ã4`ßÎÚ‡¥‘\-à¹ÖÉ"‰½ÏÞÔý#û0Îï¨ÌÑ– ŒSë*GFþ×ÉUH„ÿ¥Dr½´> Ò3ßv üoÉU,=þwÉõÿ·O ÿ¯ïUæhLÁKöáì€ë®˜%$EoØt>̪_ó˜3«†¤ó±W•Ø!©:Jº:Í-ŽMïß+G&Ó™ê4 ¸yÔ;£Õ̹È~¯œºÅ­ÿF Ò;’‚.9Ù¸ø<ÍÂÙ7%ÔgókŠÞSñ«;¥Ls!O!üê›òÔ¿š‡ÒæA¸÷T<”Ö„K™æ áì›j‡³y¨ÍãÖ{z…ZwÜJ™æþäÛ~`ŽvDØ6ÔBÜú¦W¨µãÖ}4ÍÍ€÷õ\KnåIz®“ãIz® âIz®-æIz®“è«J|/Ø´J¶îá$¿lú†u%[Ÿ½¼¯çZ2Þ×sm± ¬*AÄ:‚’­Ãz‘Ì“÷\K<÷žkƒö$'cZ‘ØäÇu3Ø|Ý=÷Љ˜V$6ù±Fö¯»¾ó=×û`²¾ay_ϵÅûJ‚&¼&×¾’d\¸{_I²(ûÆ}%A' »?øº»ãƒ%¼&[÷•U\».1+A­˜»××]·ÚWTqíºÄ¬µbî^_wïéî«JÌ@%[‡õâó47Þ×sïë¹N¢ „“$boXGP²õÅpPÉ/Ns‹c÷x{dHýßJ¬áÑ©¾#Ýâó4ëE2»çzø¾Oò™½KöÝgó;¡i@Eö@ì²:ççÃì{=Ÿ15 âs –LüÝ7j°ü`àï¾QÜ‘ÿè¹6ø ã§ô\»ä ô\oØlÏg6þÞ°»dߪ ÈJö½úÎ0h dë°^|žæ@] ÷Ø=×ÃwöɘTdÿ¶Pñ¹¸#>˜Œ×€Šì1L¯`µÎùùè{=ŸÍi@ÅçàŽ³OÒ÷N£ÆÅ0½‚Õ:ççc ïõ|†k@ÅçàY ¨¨œxV**§îÈô\|ÐñSz®]òz®7ì¶ç3ÀßvGº³Ç0nõmiTø–k´îÌ8Þ÷FÒ>dM”âwO„ÈñÁ<îž3pÒwO„ÞáÖ=îžå·zÜ=>ó¸{"”3îö¸{"ÜÑDèâó4÷c[úÀhèÛÒ8æ[®ÑÊ7˜q«ï!Œ7²&kü¦Hàr W(¸tãVŠ.U\L‘ÀeŽ_Š.üU$@I¦9G˜Œ-ŽeKÊ&%uÿÉ é‚q<Ý-;Ýåƒî`F©tÂè¡‹¬•Yù¸‹ÙÜ=î¸ç÷f$<»ï³6ftêhÛ§jFÑ©£mŸª*:u´íS5c³ÚYñÈ6ÏBd{G§þ’¬wø—dÝè‰9>ó@O„8é¬Û=z‡fUôD(>–Í=Ðá³ñ++x 'B9k0!ýSÇÿcE/§:cU_þï#)£¤‹ÏÓÜâXT¹ßyäXÂÿi?Öhèã–¾-„}Ë5êû3>ì{ã²Vf7Áƒn¾2Ë„.¬Eñ´'Âçéº']X‹âiO„Èñݱɦ  kÑA<í‰0ŸÐuOž.¬Eñ´'BïðmèªvèÂZtO{"” O7Ÿ†.¬Eñ´'Âgã*]ab]X‹âiO„rÖ ¹É().¦÷Ä…ïõKf¬jàËÉâó4—yþX£ÂFë¾-ã}Ë5’ö f÷=„q0²&ìøo 'ÂŒ7Ð!rœ4o ' Ü:o 'Bï𭼞åÂÅx=>wóz"”ó|šû±“ËóöDèÃ_E”dšS$ø±“Üå.|¯²ž±ª/GI¦¹_„Ëó70Q³cè‰Ð§Áäã— ¾W½f¬jàËß4—yþ؉›7Сï?>Í¡ÊýöíÉ?vb]Ic“­ æÏ]d=í‰Ð§Ar“QR\Ýð‰ÿ>¿lŸ±ª/ÿ‘NsÐ…µ_ÌkˆüµÜ‹gï9ÍX+˜‘IuüËÔ`û3Áõ2dNÆ‚ÜË ®9º­Ûù?&£À³÷œ‚rë›· ×vHäæÝW-Å)†‹¥˜ŒÏÞs Ê­oÞ~L72°âÎM¡sŒKý{¨!t‘pšu1œï¹>fr›Y×:oýÇNÄ4øÒ;qßþÇÿ>P §¹™?­óÖ}Âö<{Tü½õ¿¦9üÅ2A] ‡õ"Ù¿ š„?5ø¨ÿ§´ÑfßñµZ%–ºŸxÒ(áé›DVýi¼¡Ô\˜Ícï=½Â¬;öRb“¾a6½÷ô ³îØK‰¡k+˜ÿFè"‹Šô›ß{*ôë¾”dô\ƒä&£¤¨ÿž¾™¬Z×zIf»ä†ÕàHìd¿î9Åæ?zNÉÓ‹_÷œrç?zNq£Ì BOíå°j]«à'é9åÎûzNq§Lž¤ç›÷õœ’óëžSlþ£ç”<½øuÏ)wþ£ç7Ê\qôú~ûVƒ£¹äIz®OÞ×sJS&OÒs­<ïë¹Ö˯{®Oþ£ç”¢Lã„gï9ÍàÙ{NA3pby’žS€÷õœòò=§ ,ëíCfï.áÙ{NA3t<{Ï)(gòì=§ ‡OøuÏ)6ÿÑsJž^üºç”;ÿÑsŠ}ä©ýdVýi½$ &OÒsÊ÷õœâN™ùžSŠ2›Ò€–g^áZ1~ ç”e`»?¥çà ôœò~穹0UñŸ_÷œüGÏ)¯4Ú/ã×=×Êó=×zÓ‹_÷\ŸüGÏ)ÕàIýåã¯^åéØ+®³ÓÊ«Ã鼓Ÿgï9åpGxF:°2{4¢7ó—x çŠðh'^G—t(T âÙ{NA3p~ÝsŠÍôœ’§¿î9åÎôœâF™+®ù½ ùžSP–ªc<{Ï)(7À³÷œ‚fซ,•ƒÿè9Åľ†Ç'k°Tþ£ç± 67<>Ÿ½¼¯çúž½ç”ãxöžSÐ ?ÐsŠÀÂñì=§ ›øšÿè9Exfüìå}=×Çðì=§ œŽgï9å8ž½ç4cÏ5 ‰X|†.2¦9PÃùñ=×Êóâ=×zSq~|ÏõÉ‹÷œR4Áгõùñ=§Ø¼xÏ)y*Îï9å΋÷œâF¬8ûCƒO<éÇ®LÛ:ÿÑs º")R~ ç”E·ÒÕ –Êľ<ÝžSœüGÏ)‚$ûàå}=§ü@Ï)(‹É³÷œ"<Îi°TŽ Öæéöœ2º=ù†å}=§ü¸þaséÀÈëå§ôœbózNÉS"?¥ç”;o çwŒ5Eï©xŒÕŠRF"»¸§=§ü»ó¢»»üGÏ)#C©Òá?zNùx_Ï)?>Æôœ‚²è¶_<{Ï)BÇôœ‚>£Ä·ssÃôœ‚>óóì=§ œ#$ù ÿÑs êu~ ç”e€ªÏÞs Ê ðì=§ çI>ï{N±ùžSòôâ×=§ÜùžSÜè#OïÞ±êOë%Q0y’žSçwÊäIzN±y_Ï)ù Ëûz®á×=×Êó=×zÓ‹_÷\ŸüGÏ)Õ`\ê &OÒs}ò¾žSš2y’žkåy_ϵ^P §9X/’¡ÔQÌ‚Pç ;F‹Äþ!iš2#±BH: %Wì‰IÇRf$ IÕL@5ÅÿÐ#ÿ¸¬ÞƒÑý]®-¡ŠÔ{˜l%zÃßÑÃ7¬Ÿ@úcî/i[BÓ¿Ö‹ÏÓ¨‹á°^$C7@1‹Ï%2VqÃDĄ΅WÿØØ4e†W…ØØQÈQ±CBTdxu>6v,e†WÑØXõOõÒàIýå´+0º¿ƒÙªH ¬ŸA]$œæ@] ‡õ"*ÝtH-‘¡Dábý}í³7o ½¿þ"‹n‹’h¤èQRÇ„'ÚÛIkð‰'ý؉õcWÜ0á¿‚ùK¯ýº6‘†IRÍþ¢'ïMßÛdC?;¬ŸA]$œæ`½Höżcž¼ç5ÊÈV ʘڗ¡DIßH…Ë::DR)¦É;rd¸¬[‘à7HØ Ê^á†]d¿¥æB:`|‰u&.²3Ê"û'ñ°¹ï¨Ú„O¢¢B\­4°4û(éHÊîcpuIC\…Ý91 öæ“­Á^÷D öÞ'‚‚ùK¯ÌswU0‰àµ;'.þ’ƒî{•Bc sá¬J÷xû%Ý÷*…Ææâjî³ÚNû“xØÜwT m Â'ƒ½÷Eö[j.¤Æ—XgâW-e‘ öæ‹ìŸÜaé×Y+ÚÏ|Ï)Ö¡ ®VXš}”t$EqäONØv?Ž æ/¼2ÏÝU>ÐyW:½Çì+£a¬ Æ­)¦Éų÷œ‚rëÙ³:a郦}5qǪ`ü<ƒÐ&ï ûS¿5’/LÞ,übß‹š ‘ãKÄ:%‹ÙŒ²ÈþÉ–~¥ÐŽ[@\Ç4HR7JêƒÉÀ·Ô\ˆ _b‰_µ”EvFYd¡V4DÏ|Ï)Ö¡ ®c$©%U0gè"û'ñ°¹ï¨Ú„O¢¢B\Ý$7%U0ƒ½9t‘ý“;,ý:kE;â™ï9Å:”Á•´ÉMFIƒ\ç÷Ð1X/>Osüºç›ÿè9%O/~ÝsÊÿè9ÅÐ<œ½§Ä€ºÃYÊ4ǯ{®•ç?z®õ¦¿î¹>ùžSêžS(ñ¨¸6MÄ£ áÚ¿î¹Vžÿè¹Ö›^üºçúä?zNé4wàø)=×Êóz®õ¦D~JÏõÉè9¥ñ¼óq¼c)3žãU§9þ£ç”ÑA÷UKá§ôœ¼žS^'ðƒôœ<ÝžSÞina0tqìóŽG©ßDk@ÀÀ]™£'…Ù7öÚéfóØ{ß/s;ûm\|KÅþ%³oìµÓ+Ìæ±÷.ˆŒ«@à×…©K^Æ÷[H½Ï·ç¿h£hkìÉ6aö½vz…Ù<öÞ3·×B˜uÇ^Jz…Ù+ö:3ßÝõ&Ú4’Ø +s”äM´b ¾‡Ù7öÚéfóØ{ßtmã„î}Åu(ºÎ÷:L÷]qï1té¯8{BWXqö3tG+î¾¢û¯87nºâ(]±Gñ;]tÅ]4ºêÊæa—îÅð/Üïh¢ã#)?ve –h /rtƒ@ì²Ú)1›‡¬÷©xv!û†¬vJ Äæ!ë=Ž÷ ľ!«±yÈzM×N½±oÈj§Ä@l²Þãxó@ì²Ú)1›‡¬÷xÞ{˜uÇ^Jz…Ù+ö:3ÝŽ ÉÙå}1ü —MÔÀa|$åÇ®La0sfCÙ0ûÆ^;½Âl{ï‚Hôšýªµ>â4Hn2Jfߨk§W˜ÍcïýÆ:\ A.Ëm…÷A˜uÇ^Jz…Ù+ö:÷Ëw­Ì25XöbßÕN‰ØX˜}c¯^a6½÷LÑáZ³îØKI¯0{Å^gæ»»†Ù7öÚéfóØ{gae^sfCÙVƒåù‡>rK=îò] ÿÂýŽ&jà0>’òcWæµ™‚9{¡‹,qÍ~Õ®5HŽ%bV¸acÖ7KÙÚÙ7²ÍcVwd{Y)èÔï| cjÅ"³hz$¦ö̦Š#1µBdv”‰©½"³ÎÈVE§Ž–jeÀgOnaö½vz…Ù<öÞ3çA£fߨk§W˜ÍcïýÆ:\ ³W+Ó4¹\¾‹á_8ŠMÔÀa|$åÇ®Ìëú}ƒ¯ŠýæÁ÷Ž:0̺c/%½Âì{»ÓuÃÂ8Q‘<ñ_Á4¹ÐD 4:Ž ‘¸|ÿ°õŽ&jà0>’òcW&ˆ³îØKI¯0{Å^ç~×`ÙÿmT„Ù7öÚéfóØ{¡ ްÚÌ÷‰ÕlK –ço¬Ãµð?Ÿmé+9;pëj dŽV:ȳoìµÓ+Ìæ±÷~c®…Ù–,ÏgB¸nãΖ«Áò¼"Áÿ©Q’Ìõî:4xoS•ÜÆ-WƒåyE‚ÿS£çIæzw¼©JnãζÔ`y^‘àÿÔˆ)d®wסÁ{•ªDùûl¹,ÏÿŸ:оJ{? U ľ!«±yÈzW$ø?% ²wסÁû‹êC•¿fËÕ`yþÿÔöUÚë0|ô\ ľ!«±yÈzW$ø?% ²wסÁ{êC•p¥³-5Xžÿ?u }•V8 1’@ì²Ú)1›‡¬wE‚ÿS ;qw P}è·ÝRƒej ö Y픈ÍCÖ{/„Ù7öÚéfóØ{gaÂþm—«Á25û†¬vJ Äæ!ë=Žð[˜}c¯^a6½÷Ì% ×B˜}c¯^a6½÷pA“Ëå»þ+7š¨ÃøHÊ]™EªÁ²ÿÚ¨³oìµÓ+Ìæ±÷C!ùͬ;öRÒ+Ì^±×¹_Ø ÿCbßÕN‰Ø’òc'ž&|`6˜ËóŠ¿A£ $r‡¥£mSƒÖ7k§âAk޽ Êí×îÄè Û­fƒi°<¯Hð4ª@²_¾õ©ü7tuǪ”ô ]¯XÕ¹Ë]ßXÕN¯Ð5Uï7ÖŽ`©Ç†®îX•’^¡ë«:3ßÝ5t}cU;½B×AÐï|íT<è7¾÷Ñp”Dƒä&£¤aÖ{)éf¯Øë̼;‚&ç0뎽”ô ³Wìuf¾»k˜}c¯^a6½÷Ì% ׂ#h$.ßÅð/l¥ ‰8Œ¤üØ•Ùd,ûÿ Vf-¾m³oìµÓ+Ìæ±÷iïŠ%„Yw쥤W˜½b¯s¿º¯zºì«÷¡?¦Éö»ñúw †u‰»ÿ]ƒåy(#[ë¨H6ºÈBÒ?’Zßp¬Š­y8ö>Ž’hÜd”4t}cU;½B×J eà ‹,‘u¦f¿j/ D£¤ÿ?5VÆCÛ+@ÉÐìW-úuÍ D£¤ÿøk°ò?µÇÄ”?ÑÄÑóói¹\@GÏĢåðÜ•Ùì·çÉHìšXFb·2;ö3c¯­õg£Þe!öÚPöA7Ò²(½‚aóYˆ½¶ÔŸ}Щ:Uýªu²è#”DKáTªÎëæÕyݼêÊP†YºÈ†¬~–²-U—ªóßùª)1óR0gò‰ˆàGIìcbÊŸhâèùˆù´ÜîðMôÚ¿ú^ÅåP®Þð™âÈÿE—òŸ”ÛÅ4o"ŒÃìÖ‘Ž‘Ø­ÂÝU*…ßš‹ê ¾…j0Ô&±×/Å’úµ2ÏŒo¡ µÉ?îd(ì]dÌÙMHü:KÉÌ5Á’þØÇ¬”?ÑÄÑóói¹ÊHìí[¨Cmò;ÙÊ0+@Ysf($~µ3s Dð£¤?ö1+åO4qô|Ä|Zn@Á„2kxƤ9êÿèÈS5ƒCýyªraÖ&‰€ú?:òTåÖø…&Žž?mÆM=ZNEGÏŸ–+š¨Ã*”aVÐ`\êÿÆï¡?¬ŸA]$œæ@]$ûbÞM¡Ô ¦¦X2du³°º=7Øoñë?΢¿ý5xÎÔVfn˜ù¢‰< õnòì·¤¶2ó Ê0+@Y"ëLÍ~ÕŠi ‚%ý‡öñ5ØoHmeÚ‰ æÌ†.²(L}€f¿jÅ4H.%…2Ì ÐE–È:ÓF³_µbˆFIÿ¡}`tû^ãk°ß:ÚÊ´Ÿ e˜ ‹,‘u¦f¿jÅ4Á’þCûÀøì·¤¶2í E—6t1ú¯˜§…©4lh”4­ñ5ØoHmeÚݰ1+5è"šýª¥öuÍ Dð£¤ÿÐ+/[ü†YuAîÒt<ñÉŽ ³æ†Y“Ï‚Cm]$KEù%Vedöa4ûU»¹LÐ@?Jú½òz r-\—^ÌÈviké˜5I¢©HU§ªŒlvÙÃŒ§}؉¬o ¿(N‡rUÅgŒlã[K–zƒÖ¤ã®8@ºHvkìµ5 þl(#[! ±×Ö€ú³º‘î…22*"BI4°NÕ©*#ÛëÖØkk@ýÙPF¶yb¯­õgt#Ý! ed4DxÆÎóÌf!öÚPöA§êTõ«ÖyÈ¢P ,…Suª:¯›W½œob¯­õgtlh^Î÷;V4 ±×Ö€ú³×Ôšýª×@?JúÍÊ`ÄqqEêuàЩßù¦Â£ä²rYY!ÈBìµ5 þìƒÎ_JUŽ —ãÍ๠×RWpMr+3²Mr1kò£Í}åéµ]øÇeÅí@j+N¯õ 7É઻˜‘­.ÈMÞ8ŠèTª~Õ:YôJ¢¥pªNUçuóª¿Ñvá—·©­8åǹKîĬԂ\jFÒA®îpa!fÕ…¤ã¡‹dhö«vúë,š ˆFIÑ%ntNƒq©ÿ«ï¡›uAhšƒõ"[R6)(utÓ.ÕÅð/ ^8Œ¤üØ•ù¦×Ô¥ñ¨oèúƪvz…®y¬zÄâQíØ»5h}ñv*´æáØ{øšÿÏc¯­õg£Hf!öÚPöA7Ò²èæž±ó<³Yˆ½¶ÔŸ}Щ:Uýªu²è#”DKáTªßÃ͇=³ ËÆ^[êÏŽoA÷(t}cU;½B×èTª~Õ:YôJ¢¥p*§`ÏXÚ7’¯cióH¾÷8Þ÷ÖØkk@ýÙ(’Yˆ½¶ÔŸ}Ðt‡,ºyA„@ob¯­õgtªNU¿j‡,ú%ÑÀR8•SظXÚ7’¯cióH¾÷x^; ±×Ö€ú³cißH¾vJŒ¥Í#ùÞãxó,Ä^[êϾacißH¾vJŒ¥Í#ùÞãxï·Æ^[êÏF‘ÌBìµ5 þìƒn¤;dÑÍ "z³{m ¨?û SuªúUë’òcîÙ’Þcï}e‚ÔÐõUíô ]óXõ>âP$j‡³/Ãÿ^q5IÑ`ï=$ öæÆ[ nØÕÃÒ!À0»öÆ\å{ï+ o½¶ÔŸ"™…Økk@ýÙÝHwÈ¢›Dôf!öÚPöA§êTõ«ÖyÈ¢P ,…SuªÊ¦cÐïåïöÑŒ¹:öÞWf{Å|çDÊÊ]+¬LЇ2Ì è9e¢«[c¯­õg£Hf!öÚPöA7Ò²èæ½Yˆ½¶ÔŸ}Щ:Uýªu²è#”DKáTª2²Ío½¶ÔŸ •e³{m ¨?û éY¨,C!Л…Økk@ýÙªSÕ¯Zç!‹>BI4°NÕ©ê¼n^ed4w¾Yˆ½¶ÔŸ}Ð1ø^lŽ4çû« $k𤆤ê Yt[4Q$?¤Ä¬BìU}0Ñx¿zN™üHº¯Ú{ÈŠ–úK¯C×7VµÓ+tÍcÕ{æµ0;CÒ;NúGzÐ…¬èa)Qýª½‡¬b‡¥þÀÒìEox$V,öÞ}×a+L¬Ç^±É{ïØÿoØT½wá Iï±÷îs®L0 r…• V¡ë«Úéºæ±ê}ôÊ@¬ÏûÇñ¦)3žWˆãAf…U,\+vkìµ5 þlÉ,Ä^[êÏ>èFºCݼ B 7 ±×Ö€ú³:U§ª_µÎC}„’h`)œªSUF¶ù­±×Ö€ú³¡²lb¯­õgt#Ý! •e"z³{m ¨?û SuªúUëb>-ÇÍÞEÍ~ÕþÐEÝMÔÉw”t$%öÞ}ùÄá W˜¼±F5p¥Î0{tÒ¡ë«Úéºæ±ê=sÉp-ÌÎôŽ&Žž?-ÇÅÞ»ï¾2a ÈV&|„®o¬j§WèšÇª÷Õ®…Ø{ÅÒ¾‘|í”K›Gò½Çñ¾ÈûÊHì-t}cU;½B×èTª~Õ:YôJ¢¥pªNUçuó*#£¹óÍBìµ5 þ샎Á÷bs¤‘8ßïXÑC”aV¹:–&EC®ª™e˜4Å!©zÃ.²è¶h¢H~I‰Y…Ø{÷QVf-r…•Yy³{m ¨?ûw Zßp¬Š­y8ö>Ê@I4 <œ÷õœb„2Ì è9Åú‡kÓôH<ª®Af…L׊Ý{m ¨?E2 ±×Ö€ú³º‘îE7/ˆèÍBìµ5 þìƒNÕ©êW­óE¡$X §êTu^7¯2è÷r®¿jÑeЯ$_ÇRf _ÕL€2Ì ˆâÆT½aYt[4Q$?¤Ä¬Bì½û®ÉG+LÆ3d½2MÔÀ;©!©zÃ.²è¶h¢H~I‰Y…Ø{÷9'(ì Á¸ÔC×7VµÓ+tÍcÕûu¸B×7VµÓ+tÍcÕ{ ÖúÆÞ+h}ñv*´æáØ{øj‡®îX•’^¡ë«:§æû£ßf·ŽtŒÄnÅÅ}ç!”aVÐ`\ê¡«;V¥¤WèzŪÎÌww½‹¿Êÿñ{èëÅçiÖ‹dlRPêè¦o¤v—Á÷>Q¸Ìÿp¿£^8Œ¤üX£{v†¤÷Ø{_™Ý,t}cU;½B×e˜4—:š8zþ´œ^™®}CWw¬JI¯ÐõŠU™öîºëûHMËHìV†Ù5›Ž‘Ø5ŽÐwNBf Æ¥ººcUJz…®W¬êÌ|w×&üÕ ¿ä{¨ ëÅçiº8¥îÒ½þ ÆGR~ìʬ| äýŒ"ˆ}CV;%bóõ~#Ï.bßÕN‰Ø–æ~B »€¯Q¯LÐ@^ÌÂì{íô ³yì½ßX‡k!̺c/%½Âì{™ïî ëÅçiÔE²ßãªíƒI~¿ÇÀ?w›¿a58šKP„¦9X/’¡ÞýGOå eªzij£sáÕ?66M™áU!6vrT,M{"¶¯&ºš¨õ£ía½ø ]$œæ@]$CE ˜ÝæãiSïa²5–[Õ{˜ˆM…XnïBÞ÷Ž?䦓­ñQÕÆÜ÷N„©½ .þm! Teå½aÙtë÷ ýÚéxóÐï=s.\ ³3|½ÿ7P„¦9PÉÐ PÌ4÷3”áUCsf(xAK+Äóþq¼iÊŒçâxGP†®Bf®ƒ2tG2íxÔùpíXz$ ת™eè*h ŠccÕv‘E·@½4@òóH ”!Ý?ê‚Ð4]X ëE²/æPkQê¿ER‹¤ôÎ ¢ü¾”^ƒ(?ÆØñ1µÎGçÂÿŽÿ‡>õ`°50qX‘À¥<Â'„ÍvñÛ áÇ+¸”Gø„°Ù.¾oH‘À¥<Â'„Í~ýïUP\åê Ÿ¥Ì}mÅÜIðe¿èÉ´‚9“GH¼öëWoÌHìxs&(xí׎TÁœÉ#$ ^¿_gAát(W‰ø,=rpî°õÈþHÁœÉ#$ ^ûfÇ_ÌHìxü1kÞ‰þm»‚ùë+ŽdÊ#ø/<Ú‰±wüíxH:^È1¾¨~bvAYÅ‘LyÿQ‘$7%U0gt‘%ÎÏä5HŽ%½ãBÒñP†YAq$SÁÿxÔ?\›¦GâQ…píÊ0+d^áZ±ùMÍoj~Sê¦TïaÕtîÀAˆâyçãxÇRf˜ˆýþIÓ…ÉxÅ‘L·þc'Š_ßèöÁDÅ/´ÛÇ/Å‘LÑmÑç .Ms°^$C©£›äu³ljûÏ‚|ûÅ@ìŶ·ú°¬¶_ Ä^£¥xÎ@ì…&R³Ê„ÅTt[4Q$?¤Àzñyšƒ.ŽE©CC*]œ_:¿t~ij!ë—á£7t Å’ÇÈ#Șõ‹4M— Ì*„E:‚2+s…E*–£.Uç¿óUÒPÆ ±Eçã KÄEã UCVÊ0+h𤆤ª&$±~á*tñyš[ ]Ë–*Ë¢›Ž‡,ª÷”ÑF´yÚü˜A”c¢èÿÉä£Má{lª° XLÁ§eº‘Næ5™–JæWó·ôQr”ö‡©PY†g¬­<‚r»ø–úŸå{N‡rõ†Ï¼‰˜¼7ìOý‰0É}`¼RÑF÷ïmòÞ0 ¿5×cùi‚±c|=–ÿ˜&ÏÉ()tñyAhšƒ.ŽeK •eÑMÇC6Õ{Êhl´yÚü˜A”c¢èÿÉä£Má{lª°Éi1Ÿ–éØt2¯È´T2¿š¿¥’£¶=L…Ê2èFºCݼ Â3vžg6 ±×Ö€ú³:U§ª_µÎC}„’h`)œªSUFb/èâó4ëE2”úï€X7ËÒï­±×Ö€ú³Qï²{m ¨?û éY”^A„gì<Ïlb¯­õgtªNU¿j‡,ú%ÑÀR8U§ªl:ý^#š¿wØŸA¿×ˆ6¢­ÌR_Œú?:2:‚&j eó#)?v"†&Žž˜O›¡s¹ž}kìµ5 þlÔ»,Ä^[êÏ>èFºC¥Wá;Ï3›…Økk@ýÙªSÕ¯Zç!‹>BI4°NÕ©ê¼n^×Í«ÁׯmDó÷n¿bï«&Žž˜O›Á¡‰£ç#æÓfè¾=OFb¯Ø{®ôëÅçiÖ‹d(õ@¬›eév|1õtdtDìj$åÇN~4qô|Ä|Ú >>öúe¼bï«W ‰£ç#æÓf˜££¨?53t¸{ïÂ’Þa½ø<Í…®o¬j§WèšÇª÷ëp-„®o¬j§WèšÇª÷i.ëŒG}cï´¾áX;Zópì=|µ§¹xÔ?\›¦GâQ…píÊ0+dB¸Vlš;d@f…Ñù«ci‚Q4䪲ªP†Yaš ]ßXÕN¯Ð5Uï™z×Âì IïÓ\èúƪvz…®y¬zÏDv¸fgHzŸæ<ÐsÊ37¬#ÐWna0tq,º§=§<£·÷ßÄç·w–¹{á[­™›Œñ=§èXðíùìÅßÙµoü@Ÿ„¦¹…µ°^$óä=§<ƒRWÞÖ€€»2{,ºš¨ÞýCû`ev¨0 “ñ1×;ºÅèÈèÑS5C‡n1:2zôTÍàÐ-FGFžªfìß;±Ò`èi(¯ Á¸Ôoã:ÂзqC¿/vÎç\­‹=è¹üGaª—G-l‚4ù¢þ{ÚsÊ3ð›ÂyÔO{NyFäÂ7…úïiÏ)ÏhÀØŠ'‰úïiÏ)ÏhÀðÿ@ý÷´ç”g4 ´y\úïiÏ)Ïhày€ÿ¨ÿžöœòŒOéÿCãVh¢LñMÔà‘8žS{ø6.ºÅ¿Q‡×ÿFX/>/Ns~Ö=§˜ÓÔϺçúÓ\˜}c¯^a6½÷L*õfݱ—’^aöнÎÌwwæÂì{íô ³yì½gn®…0뎽”ô ³Wìuf¾»ë4ê"Ù?´ò"— þoðí`É!™º ç×=§x¿]ÿÐdœÿè9Å#ÜpƒíïÐþ£:ÐMõ÷´ç0÷´Ü­dœ7ÐsŠÇ¡7{þ¨ BÓ?Г4r–R5Ì´=íyZëÑ?5¸ ¹îBNåÙ{’FΕÿèI¹DïDÁô$\^Þד4òÁ]È©ƒ»0£øVþ£'iäà}=I#Ü…êà.Ì(¿îIù<ó¿¦9PÃa½HöżÖ=vÏ)Ÿ=yO„^ž{OÒ »çÏ ³ÆÞÿದ‡¥þTVÜ7lHú{ÖOSnß³ú›Ld8›×ä1 äE.œï9…úa³ÃpU)yͯ{Nñ$2%ƒ[1Ékž¤ç/Þs õÃf‡áªRÏXÏ)ž§kŽÃ/ßiÌ}0ɉ•Íí+ß»â^Ï^qà-ÌŠ‘ÀÊ„MÉ–ÿçWæËN·F.¬Ìw0GXα±÷ÇBHú¼BÖô°ÔŸÊŠû† Iÿٳ bÖ—7Ðs õö‡©ê0P™RA¹]<\ÖŽYó˜õ~kdëŽl)¼žS¨¶=LU‡Ê” Ê)Ž„BL­™¥Gbj¯È¬ú¯aL­XdMÄÔþ‘Ù4fUÑ©£¥ZY'Ø7~[p„ç›Ì®˜f³¾¼žS¨¶=LU‡Ê” Êíâá²v̚Ǭ÷[#[wdKá ôœBý°íaª: T¦TPNq$ bj…Èì(=S{Ef¼žS¨¶=LU‡ñÌ÷œò1µb‘Y4=SûGfÓ˜UE§Ž–jý4žñÛ¾²9G8òÙ÷®¸>Ég¯¸n ³b+³^²åÿù•Ùt»2K/ÿÑs õƒ×÷ª s4&¹üGaøö˜Õpví³ĬÀè9…úaÛ¯³6ÌÑŽUÞ@aø²Èì³Ú1k~kd{lݼžS¨¶ý:kÃíˆ"YåŠÇáu†Ë i‚^J¸ì…NýÎ×0¦6ÌŠ¥GbjG‘Ù?fEÑ©ZY'Ø7ÆÞ_¸BÒßçfE½aCÒ!©XHŠz  ßÅïÕù™­’­µëÅgP §¹in𛿦9PÃa½HÆ–”MŠ'ïI:P$‰ÕJÖ+Á‰ãìäш¤Òñ“w䘒Ĭwbw‘´0cÒ¾Doøô¿8Pû¦½ÓŽê±\p™ï«ƒ×÷êæhLÚs I@!œ}ƒVǾަL¬biíH¾y¼µChîmÌxí«@Z[/nd,lÏLFŒƒÜûä½aú1¯‘ýt€Sq÷È~:À•'Ý“4#ȽO¶îF÷ «õ!ƒØÓäéöøÀïm"ûcš¼#ÝþàÇdüwýÉ5ƒýÁìÅÅ~L±]ß(ܰûƒh}ÈrO'iÆHÊ()¬ŸA]$œæ`½HÆ&¥~£F¬V²^ NÜþc'ïhš(:Ý>yG›Jç¾PØýä Y’HìûcW\}3e¿-ª•¬7áÇÄ=kÌ ñ´`´þmß=1$…„¤öì Ió/䡸þm­d½ÿÐÆ!>0Þ|¯ñæ³7_n¼ùîÆ›b¼ùÜøMù;¾UyÀÇnãâbÊSpAù w+Ÿã¯òç·}÷Äf’ÚaÑæSÏŸ8ûµ~å˜zþÄo›jýÊ¿Ÿÿj$Ö‹ÏÓ¨‹áÐűwÏñÔ¿u`ý0Oƒb¯`WH*8¦.ÉÐñ=ÇSgév©lž¹×`ó"ãxAƒ%Xã}5XB’q¼¶~ßþ}ûj`)c,Áïß÷}5°”1 –pÌ÷Í_ ,eLƒÍ'Æ@¯Äc$¶3DöÒ` JHÐÙ@ZáC"™= °Kxöžã©gÔ6½Ö`óÌ8Þ\ƒÍ‹Œã½k°ëq¼n –d/E¿oÿ¾×«¥Œi°¿_áÕÀRÆ4XÂ1ß÷5°”1 6Ÿu&#±­˜!²N – „$û ¤>$’Ù›]³÷O+¿î9ž@þ£çøMÒ‹_÷?4ÿÑs¼|”iTÊOé9ž@Þ@Ïñ›¤D~JÏñCózŽ—@ý]ùžã©_°5꼯çxê±w’ ƒK8Ý·Dè‡y{Ýw¬æitñÔEÂi.\ìýû:°~Dœ NS§Î©ƒSÏP—dèÄ%Íj–ðì=ÇSçÊ“ôïÊûzŽ—+eò$=ÇKá}=ÇcùAzŽš§Ûs¼üô?HÏñòt{ŽßäÿÑs<õï›Û õ Érðiþ£çxêóúóúÊA÷½:¢1ù)=ÇKá ôM‰ü”žã]y=ÇË%<ºaãxþ£çxêóúóúŠpý®üGÏñÔ¿/Ø õ Þ×s<õùKB1Ü„ªœùuÏñò=Ço’^üºçø¡ùžãå£Lã`ü”žã ä ô¿IJä§ô?4o çxùÔß•ÿè9žú÷[ƒ¡>_üGÏñÔ%Ëá}=ÇSÿtªNÕ©º/(’¦‚׿À“÷O}vގǃ:ƒ\ÊA÷½l yȦªÒª´*­¥<:\ÂáÉI¨ÈžÜàN÷­úaž…~Ýw¬æiPèw*LsÐÅpX/’±IA©ÿA‡CõZê`£¤Z-õ±h£ØÎ¹Ö˜gþÞXÿã'»{!á5qøgmà?z’&pò&Žú´œ:› IßÙ’Ú³3$ÍÑÆ±𾞤 Ä/4qÔ§ÍPw3öBÂÛd›„×dà %ÈüX£!š8êÓrêl.$}oØÔž!iŽ ÖÀÄ=r?53Ôv$Ö‹ÏÐEÆ4Çô$-=ÍAÃa½HvÃôK^‡¬Ê‘ËO)d>F.o ÉJJ$—ŸRÈL’\Þ@!ÓÒ±µÂŸÜaÛ¯³w˜£ñÌ÷œBÂâ~ª>^–­OÕÇUAŒÎ`¬ÐÑàøæ‰ÞRÄÃTuO{NñÏŸ6ƒ»aÉ*ïë9å!GI×ÍÊÑ<îžS<3wHšzZNꇤç”Ï7lH*’þ(Èwñ{uÈÌÖ@ÉÖgV‹ìì]d=P¯zÚsŠ\lzû(©Ú‡4ü^U:Ë5P²u­„äâ}=Ç'¸ay_OÒrÁzñºÈ˜æ@] ‡õ"ÙM´ÇÝ“ô&36­óÖ¬‘0?Ðs|ª“}aܪ¼ôf¦Êô$=´È:Ú5nÕ@^ú?3S)ÿÑ“´| u´;¹øž¤åš ¹aú“;,ý:{§ñÌ÷œBBlïëIZ>®Ð@^ú¼¯'é¡÷ »ÄgëžÙ»ä˜£þ`½ø ê"á4]û»Òì1ñö‡©ª4Yå×…ÌÇÈ*ÿQÈd%½È*¿.d&IVùB¦¥q¿.d>Æ2YI/~]ÈL’ÿ(dZ¥ž&BÒ½öçq¼Gïr® ]|žæ  ƒÿ¡½ì1«ÃQ¿iï0G;ªzN!¶ÅÝè9…}]šæÃa½HÆ&E–5ÐcoãÞ°,ÏßXôeÇ2sBš_\²ð—äôâ×—_þ£àÒ³C8 ©¿ÿ9®é&ßv¹lþãÇNÞcÞƒ%ß´ƒYI½Y•#—ŸRÈ|Œ\Þ@!“•”H.?¥™$¹¼B¦¥÷±PÒåþ$é¶?ñ6ÿ—%â7Ù"õúšhb©×5^ßö!d‰ÊŸÿ¸…Ìüšh¿ÌüšÙó«v0<—™l:Y¢Œû®ûÛÙçuój|. E9èÜ_((ÄÊÕy|F.ÿÑsüÑ·'bìä­ˆM±“w¤û7øW#)°^|^dLs‹á°^$c“"ËþA½{Ãj°<cЗwÊÌ i~]pÉÂ\’Ó‹_\~ù‚KÏá,¤þþç8m¤›¬ †o»\ 6ÿñc'¯Áï¿:x}ÓFLS¯CVåÈå§2#—7PÈd%%’ËO)d&I.o ié},…t¹?IºíO¼ÍÿÇe‰øM¶H½¾&Z£Xêõc×·}Y¢òç?îD!3¿&Zã/3¿föüª Ïe&›N–(ã¾ë>ÄvýyݼO ¥tÎãî9þèõ_´që¿ñ{—C¹zÃg)s7­˜øÖã÷*(.‡rõ†ÏRæáÚÍ­Ì=q$Ö‹Ï‹ŒiÔÅpX/’}1/=(õÛ¸7¬Ëó7Ö}Yàqg˜…Øû¦æ×—,üGÁ%9½øuÁå—ÿ(¸ôìÎBêïŽ;†Yˆ½ïH7Ñ †o»\ 6ÿñc'¯Áï¿:x}ÓFLS¯CVåÈå§2#—7PÈd%%’ËO)d&I.o ié},…t¹?IºíO¼ÍÿÇ#±’¾¸{A‚¯Øû_å‘ý»2Ý|ì ºâ^öØ ô cÓ¥¹Ñ­<²An•K©©„<– .Yv]¬ 麾±ªWmßüÊìÑP%§Ë¤Ui(cjÙÉ*¿.¸d!«üGÁ%9½È*¿.¸ü’Uþ£àÒƒ>B½4ÀØ’˜ó• ¾™³%fý@­ýZ¨u(Cºìžù‚K–},…T<Æú†¢6T¶ù},…T<Æú†¢vL©:ŸcJÇÂ@-@¢6Ͼ‘­têw¾G‰©Ý22»ÜôHL­xdÖ¯°¶±á“.!¬í`±á>:õ;_¨—ÜõÊ|An©ûÆV&ü¨’Óa Õ„ä~]pÉBà? .ÉéEà×—_òÿQpéA¡^`lIÌŠùʬ虳ÅÓ‚KA$”‘-ûþ†PHÅãíBí§jøÕù”€§cáW- "5$ˆY?Pk¿jÊàʾC8 )1 o8kCe¢îÎBJ èÎÚ¡V[çÓ+ÔŽÅ­  ‘ÍgoÌjÍ~Õ%²Ý2f]nÊŒlÅcV¿ÂÚĆ;Lº„°¶ƒÅ†ûèÔï|  ^8pKÔ+ÓÖŒâi1Ç_åÏÜ•™¤ú3p% :ö¾›Ž‘­ÃßuÛ.ܪÁæ?~ìÄ­<²AI~gªÃB*!åBæc».V…ô ]ßXÕŽ«¶V& Ê8¶ìŒ„uøPMHà×…ÌÇÈüG!“•ô"ðëBf’äþ£iiôš¨ƖĬ˜¯L[šìkÕ)³ûþ 댹B6¹¾ñ vfOhžyÅz¹º=÷BæcÂ8ì´p,¤âAë޵Cž~„_5òT<üª_¨H $ âA)¨µ».V…ô ]ßXÕ†ÊDîºXÒ+t}cU{ô|T(Cdó]«Bz…®o¬jÔB8k•j9œÝ eˆÚk×Ū^¡ë«Úµ­”!²ÿ®‹U!½B×7Vµ=jÛ”!²ê®‹U!½B×7Vµj!œµJ‰µÎn2D­Õ®‹U!½B×7Vµ=j[„P†ÈÞv]¬ 麾±ªÐpÖ@J ¨x8ë”!j gWÌj ’uË8¦ËM—ŒdcêWXÛØp‡I—Öv°Øpú¯(ƒ+»Ü2AI |ïÊ„¡ ŒcËÎHX‡ Õ„ä~]È|Œ<À2YI/ò¿.d&Ià? ™–F¡‰`lIÌŠùÊÔà¯åN™Ý÷Ygx²yxõµ³ox5¯ÞccÝž¼ù˜à÷}Yz4 ÿý“C½¨é±'Ëf[j°<BÁHèíO"–ÇBÇVE“ òζÔ`y~Ä„22ª $ûµ?øý¢D½¨þ’îÏŠèï¡V°^|žæ@]$‹{mÃä‡eÿ7u˜¡ùæDø?û@ó·,I„¿EÓ¿ãFDÈ6}Hoï!ÔŠÇ[J½z÷A@]šæ~¬QàÙ{ŽO§âÏÞs|2Ôin1|að?4?Ðs|veÖ;~ çøM'ìÄó÷Ÿ®<{OÒ\†úÝ1œé#~Ýs¼rüGÏñIø¾j`)cŠÙ?ºyû :lö:‚9Za³øè»2AL+!¹~ìÊ„œÿèI:áʬË_rðú^Á)xÅUÛ+³ÉÁë{us4¦àÎRžÙò—¼¾WG0Gc ^qõ’ ß« ¸ÊÕ>;Üv<ô Þ˜øø_rðú^Á)xÅÕK‚~¯‚âr(Woø,eîsñ ð'wØöëìæhG„mãÖk^^_ÕWõãA߸Õj±B<¡9ºÅ/Ñà‰ƒú©ÿHøoñÁ’ïßÌÑ–,¹aãA!ô›OHÿ,ùþÁmIÁ’6ÔÞõ©9â?±û\<(àï±ç¿ÃzñÔEÂin1œ8~rnóñ¨ïÔ3ÕŽGÍyñž¤åšXú´œú³6ª3à}=IË…»G}Z®‹Œiî—ë°Ù×îæhòx,ôœBòÓ¥ûNľí" ´/® 5Øc[‰ ‘…@Ú÷Ûó ‘µióinšu1Ö‹d7LèûoM>QéR&ÿèŽziàÃø—¿zEÿX/>ƒºH8ͺH†nð[D«¥>æƒÉxþ£'ÂÍ& O{’^t‘Õj©ƒ’΀÷õD¸>ö‹6ŠË“ô$½Þדôs~Ý“ôrøž¤Ÿï^±±pº 4Í-†Çï"B®ÂTf<êËOé‰pœƒÀäõi9wú´\ÓÜ5¾>¸a§9PÉþMÐM[+­d½¿wÿßx£uŸ„Fë>$ŒÖ}¤Ö}G1Z÷}­û LÆã†Çà¸õc˜¸âÀ·óâcÇ䏨1.£âîcÖø{=ƒ­MÞöÇUÔkÔ§åÔÑý©.£¸ê5êÓf¨£ûSWP„¦9PÉPïnã«•¬7ánüø¶w­d½ÿÐFÅ·x÷¹xP˜Ñ‰}CîµoØxÐü6î¿Ô¡iÔÅpX/’±%ý‘D7ý»o˜´’‹8þÇNÞí"±ŒlßÝ+6´z6ÿÑs|Ÿ=ùµ’ëÇNr ž(h%$ïë9>'ïIz“ýc’ÈKÿgfªüGOÒCk€¬£ýX#ûŒMë¼õkŒè9> Õ B ä¥ÿ33•ò=IË×YGû±F1 ŽoþÇNÜ»>öÜ«•¬W‚ÇÿØÉ;¢I¥ã'ïÈ1%ù±F1­„äòÁÄ={µRîÛ¢ZÉz’c3Ð@©ÌŸømQ­_9 »â øžã7°+èó=Is,ê ‡‡ùž¤9–®pxæÇ´â`;ÏÞs|2ʳ÷¿éŒ:‚ÜXäG¼±F½´bòrQg¨|âI¾s¢¢‰£>-§Þ°!é‹o‰‡.†k`µl”9Â}KŒç&74qÔ§åÔ3$}ÃâÚ³3$ÍoX~Ý“tÆÊ´·tQ½P/ 6¿¥#l^ì—¡^Law„Í‹¡^(7ç¢Î>\¸")2ßÙ¾Ž 7A½48bGؼÿÑsÊ£ƒŽ‘í;³ñ$ùžSò cdûÞ°8=üGÏ)ƒtŒlßÙ‹Æôœòô cdûηÎôœ‚äAÇÈöå¸ÿÑsÊ#Ûwv×`h¿;‚¨³x¬”Ã4#Yß–è93–®to8‚¨^ûc‡iF²¾üGÏ)˜Ý09ÂæÅö›è¶7,îþMtÛh@ÿåãc¿ õÒà‰ÏÆ/½'±Œl_××èÞ½bcá—Œ’ÂzñÔEÂinÓtq,J8¿ûÉ"¹¾™g$W;’k®ÁÐËÒN®‘û©É­ññ ½ì»¶ÏÉ…&Žú´®ÁÐ˾ë÷¤ê¹Ÿšœºø<Ííû¼þ¼¾„rÐ}¯ŽhL~JÏñRx=ÇcS"?¥çxWÞ@Ïñr >§¹}Ÿ×Ÿ×—PºïÕÉOé9^ o çxlJä§ôïÊè9^.áÑÇ'¦¹Ù+Öùa)/Þs<õùgæŸ1ˆr }…Žh xyzŽ—ÂûÕs<6%€—§çxWÞ¯žãå”4Ííû¼þ¼¾„rÐ}¯ŽhL~JÏñRx=ÇcS"?¥çxWÞ@Ïñr >äæö}^^_B9è¾WG4&?¥çx)¼žã±)‘ŸÒs¼+o çx¹„GCMs¼xÏñÔ矙Æ Êö:¢%àåé9^ ïWÏñØ”^žžã]y¿zŽ—KPR°W˜æBd¯@Zçdzt¼xÏñÔ矙Æ Êö:¢%àåé9^ ïWÏñØ”^žžã]y¿zŽ—KPR 1Ð+ØëœæBd¯@Z燒:^¼çxêóÏÌ?cå@û Ñðòô/…÷«çxlJ/OÏñ®¼_=ÇË%()…èìuNsþt;?ÿÌü3Q´¯Ð-/OÏñRx¿zŽÇ¦ðòôïÊûÕs¼\‚’Rˆ^Á^ç4]XË–µÖÓ‚ò7TTE—¶–²$‘ÚêwÁ’ï ³–w}­‚7”ºQì{KÖ2êÿŒMë%A‡šá?zŽw 1ÿÇ5Þõ'ÿqbß˦?³¬/ŸÁþ`8€ÅÈKÿgf>à?z’N ²Žöc× ËûzŽÇø_AŸ„¦9PÃa½HÆ–¥þ[ä¶jÅuMÔ`C›ÿ±+³¤ÐDtª"Ë\KYò—ûᨃU9)˜³_H¼vg!5DGÏGϯý ¡ lŠ·-RC£ª`ÎB¢àµ? ×B2P0g¿(xíΚ8z>z.xíoeS¼M1꣉lHñcô(˜³_H¼vgápð:pê@8Ø<üôžÖ~JÙ’»¨!Ê.|¯|Ê̲¾ü×%…õâ3¨‹„Ó¨‹á°^$C©ÿ›¾«Á†0û±Æþ£çxŒ' s¦ ‰™ ß êZyé󾞤àb æLo¬5ð›AýCÿãNÿ«ƒ×!«(Ždþ‚ÿ£][áOþ°íAZ&†4佧źy)£\ÀlŸ‹…#¬ŸA]$œævg!%ô gíPtdNìOjòŽtÄñi'ïˆöû¦¹ÂYH‰}ÃY;U š#–ÔÜäéˆãìäÑ~ß4]Ð<œ½§Ä€ºÃYÊ “‹*Kû%ÐÅçi. y8{O‰u‡³”)Uõqá÷Ms‹dÐű_Ì» MöµêRÌ"kêS; jð×roè¶Š.s ,eÉ}i$_H‰±´o$_;E’}i$_H‰±´o$_;Æ*ö0²EYdÆyH¨ê¡Áx(üÏ5Øß§ü7F¶w]¬ 麾±ªΦÆÛ®‹U!½B×7Vµãjê «Øu±*¤Wèúƪv\M}Œ•îî÷xÈ"ë0΃µªh¢ÚþcWÜO{’VÝB‘À寥, õ³îIZÎPF¶WH÷ßSbHWß²ÅX-„¢V©xŒÕr(º5 þç!Ý[|KL‰!]E|ËcµŠZ¥â1VË¡èÖ}Ê›ÇçñDJdôžSñȨâvL©…0P«”Sj9 tk(jÿƒDFoá)1Œ*ÂS6F¶o¬k!>j•^±®åøèÖxûþæÁö ÷µºI¨³bú̓ï=úu_Êþ!±w_p½¹Äô ®Š —-®Ú>X™9f3Içƒë-È%¦WpU¹lqU•Ì­ÌbfÇb¯…ƒ.Moãþ^‡,”‘-iuúï%< ®· —˜^ÁUä²Å[ !Ô*·–CèVÏX[ÊàJZ!"Ä šL9!õþe…ÔmÜãÁë…2²]«Â\H÷ßSbHWß²ÅX-„¢V©xŒÕr(ºu¤”!ݵB¹ïý@“‰RïÝ«¹„‹ ®· —˜^ÁUä²ÅU5ánÌ•ü½ûoSÉüÿQw5n…!Ìýy/ü^‡¬: ÌÉê/eEô›ß{*ôë¾”ýc^ÿ{‚ëòŠ:”«7|öÝËž>²:þ»îPÙðï[·C}*“ÞãÂ÷¢ >³¬/RïÝ+/½Ûw¾+/½†A.²&† æì‘;„³Àø– ·#û£p-à†Š£*ÿ JÆa×ŪpPäôÆÜ½bc!è7¾÷T<è×|)ûǼ~pŰõú]÷TIß·n †ú*ê~_×[KL¯àªrÙâjêc ›Ï^ü½aCÒù Y!ÌŽÅ^ ¸fÇb¯…ƒÊ — 7&ƒ\„Þ˜Š#™¿à¿ êþ<ŽnؘuÜ=;cÖa‚Üa¾‡.ƒ.>/¤NsûÒH¾cißH¾vŒÕB(j•ŠÇX-‡¢[…ÍOsßž§’žã‘¹ay_Ïñ>GHºyPÃa½Höż7ϽçxdPê™vdv« üØ•9–,öÂʳ8fÝfߨk§W˜Ícï}wº&ÒMWë19–¤…#dÕÏ”‚ò†iÏ|a‹ÄäU4Qƒ£þŽ` f.ÏÅð/P(h¢ã#)?veŽa¶ïû,û†¬vJ Äæ!ë=Ôæq«ÙBÌú†Ù7öÚéfóØ{«÷˜Õž¨Á‚Ü÷ÆZƒO{w ®ä¾<{àÇ?Âì{íô ³yì½g2õ\!È}o¬5ø´‡qzÂì{íô ³yì½£ÞEû†~A¶òÇœä7Ѹ"̾±×N¯0›ÇÞ{¦žÃµpÌù‹žx ³oìµÓ+Ìæ±÷WÝ1kfߨk§W˜Ícï}ô<²¥Ä¬WÊŒlï1«;ö ØcbV'šÕÀ"›bßÕN‰Ø Õ¿-̾±×N¯0›ÇÞ{@íp6O­zgÝ©apu¹”ô ®W댫©&c)þ5›X¥s_³ÙÉÿ ]œ¸Ó¹„dãkíùë±2_ÂØ7dµSb 6Yïq+hðìœå!Ÿöq•ÅÏÓ>Þ©dÀ§®Ì\ÝA.%½‚ëä:j!œµJ‰µÎnÝÕ¸”áÞð‰Òàêr)é\¯ ×P á¬UJ ¨åpvk¨}ãV[Ùœ·9ã:û†¬vJ Äæ!ë=n…ƒn¤Ë >íáàêr)é\¯ ×P á¬UJ ¨åpvë®Æ­0ߟÇñBpu¹”ô ®Wë ¨…pÖ*%Ôr8»5Ô¾q«=dÎ>3ßzeÖ àêr)é\¯ ×P á¬UJ ¨åpvë®Æ­à Ÿ WwKI¯àz¹Î€Zg­Rb@-‡³[Cí·ÚÞæŒë@ì²Ú)1›‡¬÷¸º‘.4ø´‡VÁÕäRÒ+¸^A®3 ÂY«”PËáìÖ][Á¾?ã…ƒapu¹”ô ®Wë ¨…pÖ*%Ôr8»5Ô¾q«ím.ž÷ãµe:F¶›nr1¸ l^¸›ÁU`Ó1²d^aö½vz…Ù<öÞo¬q«0ûÆ^;½Âl{õfݱ—’^aöнÎÌ×,¡ï’²þ…÷E5pIù±+3WMÔਖø`eæØ7dµSb 6Yïq+ S¿óuCS5`}|”4û†¬vJ Äæ!ë=n žbßÕN‰Øû†¬vJ Äæ!ë=®Úq¼ÎVƒåù@ì²Ú)1›‡¬÷¸jÇñ ø¦Âì{íô ³yì½gB¸Ö³oìµÓ+Ìæ±÷®ÍÃì{íô ³yì½ßæãQaö½vz…Ù<öÞãQ…0ûÆ^;½Âl{ï™Ãõî0ûÆ^;½Âl{ï™láZJ˜}c¯^a6½÷ëpífݱ—’^aöнÎÌ>fߨk§W˜Ícï=S4þfn®…0뎽”ô ³Wìuf¾»ë¿‚.>/Ms?’S’㘒¬&7¬Ëó!ôey†j~{2n¨$«ÉØ «Áò|}YžÙžop·’`òný‘NsÐ…µÔ¬x_Oy0ÜÊ“ô”Ç:t‘pšûbÞí¨µ_ E¨Xò˜ËãîI™6f¥"ïë9žJt ~žã·åéö¿Yz„¤çøOçéöïÅûzÊãö@;-…¤§X™¤#ˆ¢üߛь½2!Ÿ¹÷Ù bVˆU߃î{ÕMc sqõYßXõ=è¾WÝ4¦07{cV;’ï{XúuÖM;"lÄÞ#³y$ß÷°ô묛vDØ"ˆÍ#³÷[#[w,í=’¯;%ÆÒR"ù^A+ŽUSñ u>;& SëŒÌ 鑘ZJdöŠ¥"ùŽRb,íÉ7©M#³b鑘ÚQdö]Å"[4V}ºïU7)ÌÅU@§j}ö³ù¾w¢fa©÷…úq`Ïñ¤ðgèØ“\¯/Ó;ÃéÑ›‰Fæ ĪïA÷½ê¦1…¹¸ Y·ÖOSÎ÷Õ;Éõþêݰ;R\Ðûân½¢€¿z•ÿ%ßC a½ø ]$œæ  ka½Höż†ž¼'idPêÿ¦•¾Ã ß+çg¨¾œÿè)ã«üÌ{˜˜h¢Ÿpùí=L¦co>y•?Æ{˜è‡ŸÚO£û‰¯ñUþïaBðöd|°Ãæ¾­“6 8’)þ bï{Ð}¯ºiLa.ˆÍãjnè:쫼^ÁÐR©RÉûÕSëÞÃÊ|ž1¨ŸuÏñ¤0$_åmïae¾·ÿÉ+óUø®LŸ¹cV¸acÖ7d}K¿ÎºiG„-‚Ø72k‡¬ïaé×Y7툰Eölóõ=,ý:릶bï‘Ù{Èú–~uÓŽ[±ydÖ³R4xǘZ!2;JÄÔ^‘Yg VYG)1û‡¬iL­XdMÄÔþ‘Ù4ÌŠE¶j ö²ºSb –²^A¿·àKLŃ~Á—M$¦Ö*2k9=S;™µˆµ²Z¥Ä@¬åukLí-2KLÄÔnÌÂì!²UÄÞ÷ û^uÓ˜Â\\têh©Ögw„Ox7sï³6f…õ=,ý:릶bßÈì²¾‡¥_gÝ´#ÂÙ7²µCÖ÷°ô묛vDØ"ˆ½Gfóõ=,ý:릶bóÈì=fukð,Ž1µÎȬ‰©¥Df¯@¬²ŽRb öYÓ˜Ú42+–‰©Efÿ0+Ù¢±÷=è¾WÝ4¦0Wª•u‚hï_cþ;ù•¨dzŽ'…‹yoOråmÜ­ü •Wþßø=ÔÖ‹ÏÐEÂin1ü û^uÓ˜äò=I;€ÖOCêƒI~{2þn~¤‹ŒiŽÿè)†inað/—ñ¢6ùåš¼.Ïß· 4͹ôÿP Ïâè9ž†çƵzÊóbxnÜêžã‡ÂðÜø¯»ò3T_îRùß7ÍýCáìü û^uÓ˜äò=I#c¸qòþG§QÞÿ6Íbï!«;%b)!ëô‹_5ú¾c ÓÜMôÎSqÔ_:@®ŸAzʳV )˜P²ÎIOy†¿5ã©mpêœnpêœnp꺣KŽyìGÀ…ohi?ÌÓwcýŸæ~êzÏßitÓ…‰Ãøø7æ>À•~ø¬º/î@ÜóôÝ0Í€÷õ$½$~ûwcî\ýðt«/îÀúaž¾¦_2Í-Žý?Ö˜ûWð=I/éƒÉÿ?辸cý0Oßà[Hwñyšó¸{’Öó¾O¬+±$ËÕÛ 4Xž«°K&Â÷‚'Î. |ù¾Os kS§ÖD[‰¾n~ÝžhTçGÛÝêüÿdK»Õ¥ŠQ’.°õfu|!½H8ͺHFÜNêÚž›Ö௉1Ûtjèí1åÕÛroÞÕùt»[Ýæ×ÒnUª"MÒåÁÊ>dÔ¡in1<ñ‹?óD¦¬ŸÉ7cD¥¦þãŸy"RϼQÓt1:H¯ªcêØ0ŠA¶¿Â l«Éð1õ 8è"cšu1<AK“@³iªóÃÉê"á4‹ì"YB§ñB>Ú?ml)Ây¥ /ãøy¥F+ä6òQJrù*KÂè[ÆÄ℆ÆñÈmä#ü6¯Ô(]ØPYFGØ2&ÃÝ [–„Ñ£„¿1ßGû§-¤ÂÀ"á4·ëE2Oû–Ý}‹»,®B9tqÇ_äŸÂ` U—žSµØÓ¾ÝÒ}ËŽß ‹º¸CnÜü™Ý ‘ƒ%TÅ𜪼°^|^dLs .†£Þ¡ŸD•Cþ±#Ù“:ê"á4]Œn 8‚¿#i䫉Kô9tAhšƒ.¬…õ"”‘°V(u3t “œXœ´â¨¸Š|GŽ^“?öó4ºG„0ŒU‹c?σ.;ÏÃ4‡õâ3t‘pšu1Ö‹d¨(#ao(2(fŠ£ârý]2ú<"„a¶‹íúFÇtÃî’Ñç! ã@5JëÅgP §9 Œ¡‹áPFF/£t1>G.24°”%ÑçÐEÆ4·°ºedT0~$ /£¹+[†Ñð?’Syæ3Œtñˆ-c2K¢mt#wUŒ&.¡‹ŒEÂiÃ#ª¹#$7.†CÖBýû"™0ˆ@’Ði¼Ø`gCN [FŠp^iÂËHz^©ÑŠíTf9È‘L—€œ@äH²eL,Nhh$ÍöHf9È‘Ä ç•ÅØŽ@e  $Ù2&ÃÝldH&ün6ØÙ¸ ]$„.2¦9¨5màµí<ÏóüÍó<Ïó<ó<¿ßÝîû½çe¿íyYx¡ç%ðö¼‚ÝózH!ïy![È{:Ù”Þ{ O´înÛ¶m[°¾~ýÔo~/”éf+EÑ+­ó¯0*‚2–‰X¦˜…¢cUç‹D™H–‰´L+b-o-2enEV™È.y™h™†êï£Öå)†u1”9e|1*ã2"QLe’>J1+ó2±eâeúµê–TìrÙ;æ}ˆm: G‡QZÊ$°L‚eʉ` •I¸Lb™¬ÒS”¤L‡‘URe’.“\&ÎJ—_VJ™Ã>Óíþ•ª~'ÆR¡L*–Ie™TËtè±R+“z™IZº´2u›ëRZé”I·Ìi¿-µ‡KO™ô–I¿Ìä÷:"¨¥nªŒ2™ejʪ'fYÊDRú–•2Y-“u™Þ/[™ìeÊøreòe"Ö.äÜxTW¡Li%šA¨ ¥VD™"Ëüü´,.–üýz* eŠb™¢,SÔ2E—98³beŠ—)µHzWI™`K]J¥”)µLé2å"°²27aÝ¡•«üåTŽ2Ÿ©œÊ”s™IÀ¬r•¹éã=zÒÊM™r[¦ÜË|¶öÓ,RyÊ”·LùeÊ­È’ÈMIPE™J"UŒéÖÈT‰2ÀW%•©ä2•–ùDkª¬LïéT¹2•/S-SÔÍW+s ~¯®Ìqt1¢Býiõ(S”.솞\=)SOËÔs™zËÔ/sÎRº…èÒ‹ì~ò¡]ò Õó j Ì%zPƒÊ4¸LÃ2ešeÊ}¨R)Šýt9êµm-”¹ÙQ;NKj‘2-Z¦Åeê铺ѵ¤ÌOÛ%>Z-¥LK-ÓÒeZ.3ÑÿZ•ù %÷Yä­5eZ[¦õ2í2%ö:¹Y …&ñØZlm-:¼˜¦v³u€2°L–é`™É ®Ce:\¦c™Î23ßµKXo¸ëcÎ*¯·.(Ó…eºX¦Ë2]Ët—™§æÝmzʤ\¦W™NÁôVæè7½+Óû2ÅPÓW¦J4çÇL?eZ°2ý*ÓòÍÜz0ý¦L¿-Óïe*ØLeú3ý+Óÿ21àæšŠæ”ñf”)»‰s q“´ó(f‚2–™X¦_JÍ-‡1—;`öfÊLb™I–™´L=¶1“•)'‚fre&_fZ&«ô˜„‘eà°™Qff™Ù2ó2[æòtÌe„Ìaws»¥šTuæÄh”©ÏŠéß3æ e:ô˜9¨2]f’–6+sð2—>lÒ6g)sšš9T™C—é·Žæ*ys˜2‡-sx™›B`ŽÊÔôÄ+Ó|ÜDôfÒWf:8ć2DZÌq–9n™ãË”q&ïÇœ”‰œa.œRæÔ2§Ë”Î0ge&”jΕ9_æç¡M¹Lò×\”éAÒ|H¦‹Ì§J˜K(sp¦¹De.q™K–¹´Lï60—•IÝÕ”‚5—SærË\¾L71We®Ë”ï½¹œæó¤æ‘£›Ê%ÁÜ LÌ6mn¨Ì —¹±Ìãh²~¶¹I™ŸfsSenºL9Ë­¹Y™$ƒ˜›+sóe*Égb즑1·(sË2·–IדÔÍmÊܶÌíeîeÊÍSä'MQé57¿óP¦ß©`ŽFæ‘2ýfC&RQšG)ó¨e>Ù…æÑÊ“ùP‹šÈþ”)ú¨¦Ü©4”ùé–˜^ã4·ÝÌ|ˆ2²Ì‡–ùp™rè©ù¨Ì.0+óñ2—X^óhÏbŽ›h>IÍ'”ùÄ2ýÿ„ù¤2Ÿ\æÓ2•l±9”¥˜Ï”)I¢™œAæ³”ù¬e>»Ìçe¾eŽ£^3«¼9u¸ùQ¦µÇ˜ÉDÁ4øXóe~²ÌY¦ù©2]nØü´2?½ÌÏe~ËüËlûÚùÝ-Èím\âµ+ËÌ5†üîÆÐVm[·ØVë¤#HÏ$Éöðÿÿo·ívØjÚöÙ>ÛgoÞ¶núõê‰a+Bµõ»ËžzçzêÝ¢§Þq{ꥷ§^z{b Œp¬‡·§^‘=õŠÚSobO½›ï©wómÛ¶[n¹å–[n¹å–[n¹å–[n¹å–[n¹å–[n¹å–[’Þ’4é-·ÜrË-·ÜrËË}]×¥¸[n¹å–[n¹å–[n¹åzË-·ÜrË-·ÜrË-·ÜrË-·T¸Ýîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿mÛ¶mÿ¶NáOGbî1 –·ZÜNa„ªóc¬nDEám‹W]àÌ9¿??B Ÿ$¥vØÇh­˜¡8»ÔÉôäm§»¡>ëö1ùøÜ ° Ð:p< <HÁ ²ïØ;ðN¿ä7Á;¨7k[ÙôØôm¹ìÍÛ–äj®æj®æj®æj®æ[æÛäçÏ t”V)(pæó˜¶9•:s¥ó˜”nƒ ºô_·$¦ÏqçàsmO„–@ÛöÓ .òíÒW|] 1m|Nú|®í‰ðSжýô‚Kí.} ¬û’˜6>•v®í‰0Ú¶T\~ZÞ¥¯øº6‰iã »smO„CAÛöÓ .IÝ»ôýX7'1m\e8ܹ¶'B‹¡m+÷‚Ë ¹»ô_÷NbÚ8BÛî\ÛáphÛ~zÁåJ—¾ëºIL_" smO„3hÛʽàrãÕ¥¯u)$¦ûÅx;×öD¨Ú¶T\åìÒ÷(ë^$¦ËÃ@a®í‰Dm[Ñ— ]úНë$1müÓm»smO$hÛ~zaÃï¨K_¿ÖHLŸÂÀ;×öDB…¶­WaÃci—ûº?‰iïìì…öDbkææZJòr%¶­Ü –G¬K}ݔĴwvöB{"1¾ss-%ù‡Lþl[¹6< Ú¥À¾®‰iïìì…öDb°Ì͵”ä™ÉŸm+÷†©¨v)°¯‹’˜öÀÎ^hO$`n®¥$¿ùä϶•{aìÌw)°¯«’˜öÀÎ^hO$&˜›k)Éo>ù³må^ذ¥Žu)°¯;ObÚ;;{¡=‘ `n®¥$¿ùä϶•{aÃ2 ] „×#1íý…±±æí‰„VLPñõe³%~ÂÙ¶Ÿ^Ø0‹U—áu-˜önƒªöÞžHð= ”ŠY/w]ölÛO/lXŠå.ÂëZ‘˜öþÂØXóöDâsI âë9 •zªç³m?½°á¤[»¯k™Ä´wTµ÷öDBD¥bÖËÝe=ÛöÓ v€C—áu·’˜öþÂØXóöDB~I âë9 •zªç³m?½°a¿¹u)°ºîÄ´÷ÆÆš·'Ï. T|=G¡ROõ|¶-•… /±Kàº7ÓÞ_kÞžH S¨øzŽB¥žêùl[d6,‡¢KàºDÓÞ_kÞžHl¾d軞£lö©%:ÛÙ… ‚­KຠÓÞ_kÞžH ºô¡ìêE”s"϶EvaØ »0_—Ä´÷ÆÆš·'-ýv›õÅE%Xζ]ذK] „×5$1íý…±±æí‰„¥ßn³ž£<äSòÙ¶Ÿ^8†b½KàºþHL{al¬y{"ñ‰Òo·YÏQò)ùl[dŽ9`èR ¸®uÓÞ_kÞžH`*ýv›õåqÙ³m‘]8Fžu—áu1˜öþÂØXóöDÂeé·Û¬ç(›Ò¿=ÛöÓ Ç<Ä”.V×]“˜öþÂØXóö$í.ýv›õeSzâ·gÛRY8†Šã»¯;…Ä´÷ÆÆš·'é±b‚Нç(›-±Ü϶ýôÂ1Iþ]úúµîˆÄ´q„¶Ý¹¶'iëжõ*£ÉÓ¥/ëŽ'1m| Ãî\Û“4BhÛ>´p a—¾H¬+‰iãSvçÚž¤Å¡mûÐÂ1$ºôEb]„$¦OaØk{’€¶íC Çl@ºK_$ÖE‚Ä´ñ) »smOÒ¡mûÐÂ1ÏÒ¥/ë’&1m| Ãî\Û“´Ãж}há˜OûèÒ‰uBbÚø†Ý¹¶'éeж}hád té‹Äº$¦OaØk{’Ö‡¶íC Lj*Þ¥/ë 1m| Ãî\Û“t’ж}háúÕ¥¯uÅILãÄçÚž¤§¡mKeá˜%Ú²K_ëúEbÚ¸uÚ¹¶'iGhÛRY8FŽåvéë׺[’˜6þP¥smOÒ&´m½ Ç(y°.}ýZw¹$¦#dØk{’öm[¯Â1džO†. ?^¾¡Âð£†ë-X».X»¬`í‚µÔ Öú ¢ \ÁÚ‡¬=¶l|!Y’…džB2d É6+$Ó[0X¼`ðÃE ¦S0øS…cÝ…c•ŽÕ ÇÊU8£Â±Ÿ(ˆ‚XADÁ[Aü^9 ¢0+Ì…™Ã…™V˜1 ³…Ù’…ÙP…Ì«)þ£·)êp+†:Ü¢¨Ã­Š:ÜΣ·c¨Ã­êp»u¸= ·7Ôá–ˆ:Ü*P‡[6ÔáÖu¸õ‡:ÜZGn1 ·kÔá¶°tŒmÝŸŠm)P¯‚ò”‚K770 aÿt¬ø·ö/¼1–;L770 aÿt¬ø·ö/¼1–«´›†°:V–­7'Qª²nn`›“ÊÈ@|\ÙšjFF770 áÍQ}l½9©ˆR•usÃöOÇŠkš2ú †Ônn`ÂþéXño=a¡ÆusÃ~XYÆÚÁ1WÖÍ CØ?+þ­ývˆÔnn`ÂˈCÖ4e •Iíæ†!¬C+Û‹Lªƒ{770 áie n¹ºa— ÓÍ CxZÙ‚[®nØeJ»¹aO+[pËÕ »Lnn`ÂÓÊÜruúÉÓÍ CxZÙ‚[®N?y¥ÝÜÀ0„§•-¸åêô“×£›†ðÃ:(§Á>«Ö¿nn`Âþ9è œø<¬nXÿº¹a?ì ƒz ¦ÇçauÃú×Í CØ?Ôc0=>«Ö¿nn`Â;è ƒéñyXÝcÛ»¹a?ì ƒz ¦ÇçÍQÛ\770 á‡tPÁôøÜÍ CøaT:˜œ>wsÃ~ØA‡Œýs770 á‡tp˜Á”Êés770 á‡tp˜Á”~ÖÒ4ªÝÜÀ0„vÐÁaSú¹›†ðÃ:8Ì`Ÿ»¹a?ì ƒƒ±úùaÕI770 á‡tp`0V??<=ÝÍ Cøa”Ó`ŸýÓ±²¬›†°¾2DäR÷5ÕŒ FF770 áÍMtËU*§nn`››è–«”`770 áÍMtËUÊÞÍ CxsÝr•²ÚÍ CxsÝrõèæ†!¼¹‰n¹z ÓÍ CxsÝrõPÚÍ CxsÝrõУ›†°ÎÒ››^?Œˆœ²nn`ÂË ©kªŒŒnn`ÂBÜãëÍIE”J(a770 aV”-ßõæ¤"JUÖÍ CxXAVõX¯×ëõº†Öm+À Ç¨¿6½w×/È%¶£>F}Œºš·mÛ¶­X[‡IJ¥Y:kÛ¶µ†ÊŸNeÛ޼ζmÛ¶mÛ¶]Æ`H]ÓGÎpÐOÛ¶mÛ¶[Û¶Õ±Ò¶mÛ~¶íàgÛ¶mÛ¶mÛ¶mmÛ¶¶=?º·èz½^¯×ëõz½^¯×ë5¬×ëõÖëõz½^¯×ëõz½^CÛ·m{ž-´mÛ¶mÛÚ¶mÛ¶mÛ¶mk·mí¶-Ú¶}Û¶·mß¶- mû¶mQhÛ·mÛ¶-S`zävÓ¶}_¦ÀôPäbÛ÷e LÔµ}_¦Àô,–Ðö}™Ó³DTXBÛ÷e LÏQa mß7ŸïÑÚ}߾߳mß7ŸïÆvɾoߎ¯Ë¶}ß||>¾Û%û¾ùø||7¶Kö}óñùønl—ìûæãóñ]âvP}ß||>¾[´rÓ÷ÍÇçãû¨õô}óñùøÞ’û¾ùø||µ,}ß||>¾È[ľo>>_³õÛ¾o>>_³õÛ¾o>>ߣµ¢û¾mÛ¾}Û¾}Û¾}Û¾}Û¾}Û¾}Û¾}Û¾}Ûo[´mënÛ¾mÛÚÔ¯¶`½Œ¶[¶…‡–Ѷíy¶mÛ¶mÛ¶mÛ–äÄ¡mÛ¶mÛ¶=ÏólÛ‚ò´¾Ý²§<÷¼ÚS‹^âöÊ›v_xö힇epython-ipmi-0.5.4/tests/interfaces/000077500000000000000000000000001436677633700173105ustar00rootroot00000000000000python-ipmi-0.5.4/tests/interfaces/__init__.py000066400000000000000000000000001436677633700214070ustar00rootroot00000000000000python-ipmi-0.5.4/tests/interfaces/test_aardvark.py000066400000000000000000000033261436677633700225200ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- class MockPyaardvark: def enable_i2c_slave(self, d): pass class TestAardvark: pass # @classmethod # def setup_class(self): # """Mock pyaardvark import # http://erikzaadi.com/2012/07/03/mocking-python-imports/ # """ # self.pyaardvark_mock = MagicMock() # self.pyaardvark_mock.open.return_value = MockPyaardvark() # modules = { # 'pyaardvark': self.pyaardvark_mock, # 'pyaardvark.open': self.pyaardvark_mock.open, # } # self.module_patcher = patch.dict('sys.modules', modules) # self.module_patcher.start() # ok_('pyaardvark' in sys.modules.keys()) # ok_('pyaardvark.open' in sys.modules.keys()) # from pyipmi.interfaces.aardvark import Aardvark # self.my_aardvark = Aardvark() # @classmethod # def teardown_class(self): # """Let's clean up""" # self.module_patcher.stop() # def test_rx_filter(self): # header = IpmbHeader() # header.rs_lun = 0 # header.rs_sa = 0x72 # header.rq_seq = 2 # header.rq_lun = 0 # header.rq_sa = 0x20 # header.netfn = 6 # header.cmdid = 1 # rx_data = (0x1c, 0xc4, 0x72, 0x08, 0x1, 0x85) # ok_(self.my_aardvark._rx_filter(0x20, header, rx_data)) # def test_inc_sequence_number(self): # self.my_aardvark.next_sequence_number = 0 # self.my_aardvark._inc_sequence_number() # eq_(self.my_aardvark.next_sequence_number, 1) # self.my_aardvark.next_sequence_number = 63 # self.my_aardvark._inc_sequence_number() # eq_(self.my_aardvark.next_sequence_number, 0) python-ipmi-0.5.4/tests/interfaces/test_ipmb.py000066400000000000000000000075261436677633700216620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from array import array from pyipmi import Target from pyipmi.interfaces.ipmb import (checksum, IpmbHeaderReq, IpmbHeaderRsp, encode_send_message, encode_bridged_message, encode_ipmb_msg, decode_bridged_message, rx_filter) def test_checksum(): assert checksum([1, 2, 3, 4, 5]) == 256-15 def test_header_req_encode(): header = IpmbHeaderReq() header.rs_lun = 0 header.rs_sa = 0x72 header.rq_seq = 2 header.rq_lun = 1 header.rq_sa = 0x20 header.netfn = 6 header.cmdid = 1 data = header.encode() assert data == b'\x72\x18\x76\x20\x09\x01' def test_header_req_decode(): header = IpmbHeaderReq() header.decode(b'\x72\x19\x76\x20\x08\x01') assert header.rs_sa == 0x72 assert header.rs_lun == 1 assert header.rq_sa == 0x20 assert header.rq_lun == 0 assert header.netfn == 6 header = IpmbHeaderReq(data=b'\x72\x19\x76\x20\x08\x01') assert header.rs_sa == 0x72 assert header.rs_lun == 1 assert header.rq_sa == 0x20 assert header.rq_lun == 0 assert header.netfn == 6 def test_header_rsp_encode(): header = IpmbHeaderRsp() header.rs_lun = 0 header.rs_sa = 0x72 header.rq_seq = 2 header.rq_lun = 1 header.rq_sa = 0x20 header.netfn = 6 header.cmdid = 1 data = header.encode() assert data == b'\x20\x19\xc7\x72\x08\x01' def test_header_rsp_decode(): header = IpmbHeaderRsp() header.decode(b'\x72\x19\x76\x20\x08\x01') assert header.rq_sa == 0x72 assert header.rq_lun == 1 assert header.rs_sa == 0x20 assert header.rs_lun == 0 assert header.netfn == 6 header = IpmbHeaderRsp(data=b'\x72\x19\x76\x20\x08\x01') assert header.rq_sa == 0x72 assert header.rq_lun == 1 assert header.rs_sa == 0x20 assert header.rs_lun == 0 assert header.netfn == 6 def test_encode_ipmb_msg(): header = IpmbHeaderReq() header.rs_lun = 0 header.rs_sa = 0x72 header.rq_seq = 2 header.rq_lun = 0 header.rq_sa = 0x20 header.netfn = 6 header.cmdid = 1 assert encode_ipmb_msg(header, b'\xaa\xbb\xcc') == \ b'\x72\x18\x76\x20\x08\x01\xaa\xbb\xcc\xa6' def test_encode_send_message(): data = encode_send_message(b'\xaa\xbb', 0x12, 0x20, 7, 0x22) assert data == b'\x20\x18\xc8\x12\x88\x34\x47\xaa\xbb\x86' def test_encode_bridged_message(): payload = array('B', b'\xaa\xbb') t = Target(0) t.set_routing([(0x81, 0x20, 7), (0x20, 0x72, None)]) header = IpmbHeaderReq() header.netfn = 6 header.rs_lun = 0 header.rq_seq = 0x11 header.rq_lun = 0 header.cmdid = 0xaa data = encode_bridged_message(t.routing, header, payload, seq=0x22) assert data == \ b'\x20\x18\xc8\x81\x88\x34\x47\x72\x18\x76\x20\x44\xaa\xaa\xbb\x8d\x7c' def test_decode_bridged_message(): # 81 1c 63 20 14 34 00 20 1c c4 82 14 34 00 20 14 cc 74 14 22 00 ed ff 6a 36 data = b'\x81\x1c\x63\x20\x14\x34\x00\x20\x1c\xc4\x82\x14\x34\x00\x20\x14\xcc\x74\x14\x22\x00\xed\xff\x6a\x36' data = decode_bridged_message(data) assert len(data) == 9 assert data == b'\x20\x14\xcc\x74\x14\x22\x00\xed\xff' def test_rx_filter(): header_req = IpmbHeaderReq() header_req.rs_lun = 1 header_req.rs_sa = 0x72 header_req.rq_seq = 2 header_req.rq_lun = 0 header_req.rq_sa = 0x20 header_req.netfn = 6 header_req.cmdid = 1 # requester and responder fields are twisted ... (sa and lun) header_rsp = IpmbHeaderReq() header_rsp.rs_lun = 0 header_rsp.rs_sa = 0x20 header_rsp.rq_seq = 2 header_rsp.rq_lun = 1 header_rsp.rq_sa = 0x72 header_rsp.netfn = 6 + 1 header_rsp.cmdid = 1 rx_data = encode_ipmb_msg(header_rsp, b'\xaa\xbb\xcc') assert rx_filter(header_req, rx_data) python-ipmi-0.5.4/tests/interfaces/test_ipmitool.py000066400000000000000000000176151436677633700225670ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from mock import MagicMock from pyipmi.errors import IpmiTimeoutError, IpmiConnectionError from pyipmi.interfaces import Ipmitool from pyipmi import Session, Target from pyipmi.utils import py3_array_tobytes class TestIpmitool: def setup_method(self): self._interface = Ipmitool(interface_type='lan') self.session = Session() self.session.interface = self._interface self.session.set_session_type_rmcp('10.0.1.1') self.session.set_auth_type_user('admin', 'secret') self._interface.establish_session(self.session) def test_build_ipmitool_target_ipmb_address(self): target = Target(0xb0) cmd = self._interface._build_ipmitool_target(target) assert cmd == ' -t 0xb0' def test_build_ipmitool_target_routing_2(self): target = Target(routing=[(0x81, 0x20, 7), (0x20, 0x82, 0)]) cmd = self._interface._build_ipmitool_target(target) assert cmd == ' -t 0x82 -b 7' def test_build_ipmitool_target_routing_3(self): target = Target(routing=[(0x81, 0x20, 0), (0x20, 0x82, 7), (0x20, 0x72, None)]) cmd = self._interface._build_ipmitool_target(target) assert cmd == ' -T 0x82 -B 0 -t 0x72 -b 7' def test_build_ipmitool_target_routing_4(self): target = None cmd = self._interface._build_ipmitool_target(target) assert cmd == '' def test_send_and_receive(self): pass def test_rmcp_ping(self): mock = MagicMock() mock.return_value = (b'', 0) self._interface._run_ipmitool = mock self._interface.rmcp_ping() mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' '-U "admin" -P "secret" ' 'session info all') def test_send_and_receive_raw_valid(self): mock = MagicMock() mock.return_value = (b'', 0) self._interface._run_ipmitool = mock target = Target(0x20) self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' '-U "admin" -P "secret" -t 0x20 -l 0 ' 'raw 0x06 0x01 2>&1') def test_send_and_receive_raw_lanplus(self): interface = Ipmitool(interface_type='lanplus') interface.establish_session(self.session) mock = MagicMock() mock.return_value = (b'', 0) interface._run_ipmitool = mock target = Target(0x20) interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lanplus -H 10.0.1.1 -p 623 ' '-U "admin" -P "secret" -t 0x20 -l 0 ' 'raw 0x06 0x01 2>&1') def test_send_and_receive_raw_no_auth(self): mock = MagicMock() mock.return_value = (b'', 0) self._interface._run_ipmitool = mock self._interface._session.auth_type = Session.AUTH_TYPE_NONE target = Target(0x20) self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I lan -H 10.0.1.1 -p 623 ' '-P "" -t 0x20 -l 0 raw 0x06 0x01 2>&1') def test_send_and_receive_raw_return_value(self): mock = MagicMock() mock.return_value = (b' 10 80 01 02 51 bd 98 3a 00 a8 ' b'06 00 03 00 00\n', 0) self._interface._run_ipmitool = mock target = Target(0x20) data = self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') assert data == b'\x00\x10\x80\x01\x02\x51\xbd\x98' \ b'\x3a\x00\xa8\x06\x00\x03\x00\x00' def test_send_and_receive_raw_completion_code_timeout(self): mock = MagicMock() mock.return_value = (b'Unable to send RAW command (channel=0x0 ' b'netfn=0x6 lun=0x0 cmd=0x1 rsp=0xc3): ' b'Ignore Me\n', 1) target = Target(0x20) self._interface._run_ipmitool = mock data = self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') assert data == b'\xc3' def test_send_and_receive_raw_completion_code_not_ok(self): mock = MagicMock() mock.return_value = (b'Unable to send RAW command (channel=0x0 ' b'netfn=0x6 lun=0x0 cmd=0x1 rsp=0xcc): ' b'Ignore Me\n', 1) target = Target(0x20) self._interface._run_ipmitool = mock data = self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') assert data == b'\xcc' def test_send_and_receive_raw_timeout_without_response(self): mock = MagicMock() mock.return_value = (b'Unable to send RAW command ' b'(channel=0x0 netfn=0x6 lun=0x0 cmd=0x1)\n', 1) target = Target(0x20) self._interface._run_ipmitool = mock with pytest.raises(IpmiTimeoutError): self._interface.send_and_receive_raw(target, 0, 0x6, b'\x01') def test_send_and_receive_raw_serial(self): interface = Ipmitool(interface_type='serial-terminal') self.session.set_session_type_serial('/dev/tty2', 115200) interface.establish_session(self.session) mock = MagicMock() mock.return_value = (b'', 0) interface._run_ipmitool = mock target = Target(0x20) interface.send_and_receive_raw(target, 0, 0x6, b'\x01') mock.assert_called_once_with('ipmitool -I serial-terminal ' '-D /dev/tty2:115200 -t 0x20 -l 0 ' 'raw 0x06 0x01') def test_parse_output_rsp(self): test_str = b' 12 34 56 78 \r\n d0 0f af fe de ad be ef\naa 55\r\nbb \n' cc, rsp = self._interface._parse_output(test_str) assert cc is None assert py3_array_tobytes(rsp) == b'\x12\x34\x56\x78\xd0\x0f\xaf\xfe\xde\xad\xbe\xef\xaa\x55\xbb' def test_parse_output_rsp_suppressed_error(self): test_str = b'Get HPM.x Capabilities request failed, compcode = c9\n'\ b' 12 34 56 78 \r\n d0 0f af fe de ad be ef\naa 55\r\nbb \n' cc, rsp = self._interface._parse_output(test_str) assert cc is None assert py3_array_tobytes(rsp) == b'\x12\x34\x56\x78\xd0\x0f\xaf\xfe\xde\xad\xbe\xef\xaa\x55\xbb' def test_parse_output_cc(self): test_str = b'Unable to send RAW command (channel=0x0 netfn=0x6 lun=0x0 cmd=0x1 rsp=0xcc): Ignore Me\n' cc, rsp = self._interface._parse_output(test_str) assert cc == 0xcc assert rsp is None def test_parse_output_cc_suppressed_error(self): test_str = b'Get HPM.x Capabilities request failed, compcode = c9\n'\ b'Unable to send RAW command (channel=0x0 netfn=0x6 lun=0x0 cmd=0x1 rsp=0xcc): Ignore Me\n' cc, rsp = self._interface._parse_output(test_str) assert cc == 0xcc assert rsp is None def test_parse_output_connection_error_rmcp_plus(self): test_str = b'Error: Unable to establish IPMI v2 / RMCP+ session\n' with pytest.raises(IpmiConnectionError): cc, rsp = self._interface._parse_output(test_str) assert cc == 0xcc assert rsp is None def test_parse_output_connection_error(self): test_str = b'Error: Unable to establish LAN session' with pytest.raises(IpmiConnectionError): cc, rsp = self._interface._parse_output(test_str) assert rsp is None def test_parse_output_connection_error_rmcp(self): test_str = b'Error: Unable to establish IPMI v1.5 / RMCP session' with pytest.raises(IpmiConnectionError): cc, rsp = self._interface._parse_output(test_str) assert rsp is None python-ipmi-0.5.4/tests/interfaces/test_rmcp.py000066400000000000000000000110321436677633700216570ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import array from pyipmi.session import Session from pyipmi.interfaces.rmcp import (AsfMsg, AsfPing, AsfPong, IpmiMsg, RmcpMsg) from pyipmi.utils import py3_array_tobytes class TestRmcpMsg: def test_rmcpmsg_pack(self): m = RmcpMsg(0x7) pdu = m.pack(None, 0xff) assert pdu == b'\x06\x00\xff\x07' m = RmcpMsg(0x7) pdu = m.pack(b'\x11\x22\x33\x44', 0xff) assert pdu == b'\x06\x00\xff\x07\x11\x22\x33\x44' def test_rmcpmsg_unpack(self): pdu = b'\x06\x00\xee\x07\x44\x33\x22\x11' m = RmcpMsg() sdu = m.unpack(pdu) assert m.version == 6 assert m.seq_number == 0xee assert m.class_of_msg == 0x7 assert sdu == b'\x44\x33\x22\x11' class TestAsfMsg: def test_pack(self): m = AsfMsg() pdu = m.pack() assert pdu == b'\x00\x00\x11\xbe\x00\x00\x00\x00' def test_unpack(self): pdu = b'\x00\x00\x11\xbe\x00\x00\x00\x00' msg = AsfMsg() msg.unpack(pdu) def test_tostr(self): m = AsfMsg() m.data = b'\xaa\xbb\xcc' assert str(m) == 'aa bb cc' class TestAsfPing(): def test_pack(self): m = AsfPing() pdu = m.pack() assert pdu == b'\x00\x00\x11\xbe\x80\x00\x00\x00' class TestAsfPong(): def test_unpack(self): pdu = b'\x00\x00\x11\xbe\x40\x00\x00\x10\x00\x00\x11\xbe\x00\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00' m = AsfPong() m.unpack(pdu) class TestIpmiMsg: def test_ipmimsg_pack(self): m = IpmiMsg() pdu = m.pack(None) assert pdu == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' def test_ipmimsg_pack_password(self): s = Session() s.set_auth_type_user('admin', 'admin') m = IpmiMsg(session=s) psw = m._padd_password() assert psw == b'admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' s = Session() s.set_auth_type_user(b'admin', b'admin') m = IpmiMsg(session=s) psw = m._padd_password() assert psw == b'admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' def test_ipmimsg_pack_with_data(self): data = py3_array_tobytes(array.array('B', (1, 2, 3, 4))) m = IpmiMsg() pdu = m.pack(data) assert pdu == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x01\x02\x03\x04' def test_ipmimsg_pack_with_session(self): s = Session() s.set_auth_type_user('admin', 'admin') s.sequence_number = 0x14131211 s.sid = 0x18171615 m = IpmiMsg(session=s) pdu = m.pack(None) assert pdu == b'\x04\x11\x12\x13\x14\x15\x16\x17\x18admin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' def test_ipmimsg_pack_auth_md5(self): s = Session() s.set_auth_type_user('admin', 'admin') s.sid = 0x02f99b85 m = IpmiMsg(session=s) sdu = b'\x20\x18\xc8\x81\x0c\x3a\x02\x04\xe1\x2c\xb4\xd3\x17\xdc\x40\xdf\xe9\x78\x1e\x6d\x8e\x10\xad\xeb\x2c\xe8\x5c\xa0\x5b' auth = m._pack_auth_code_md5(sdu) assert auth == b'\x40\x46\xb1\x51\x4c\x89\x7f\x73\xc2\xfb\xa7\x4d\xf8\x03\x73\x8c' def test_ipmimsg_unpack(self): pdu = b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x00' m = IpmiMsg() m.unpack(pdu) assert m.auth_type == 0 assert m.sequence_number == 0x11223344 assert m.session_id == 0x55667788 def test_ipmimsg_unpack_auth(self): pdu = b'\x01\x11\x22\x33\x44\x55\x66\x77\x88\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x00' m = IpmiMsg() m.unpack(pdu) assert m.auth_type == 1 assert m.sequence_number == 0x11223344 assert m.session_id == 0x55667788 assert m.auth_code == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] class TestRmcp: # def test_send_and_receive_raw(self): # mock_send = MagicMock() # mock_recv = MagicMock() # mock_recv.return_value = (b'\x06\x00\xee\x07\x00\x00\x00\x00\x00\x00' # b'\x00\x00\x00\x06' # b'\x01\x02\x03\x04\x05\x06', 0) # target = Target() # target.ipmb_address = 0x20 # rmcp = Rmcp() # rmcp.host = '10.10.10.10' # rmcp.port = 637 # rmcp._sock.sendto = mock_send # rmcp._sock.recvfrom = mock_recv # rmcp.send_and_receive_raw(target, 0, 0, b'\x00') # rmcp._send_ipmi_msg.assert_called_with(1) def test_send_and_receive(self): pass python-ipmi-0.5.4/tests/msgs/000077500000000000000000000000001436677633700161365ustar00rootroot00000000000000python-ipmi-0.5.4/tests/msgs/__init__.py000066400000000000000000000000001436677633700202350ustar00rootroot00000000000000python-ipmi-0.5.4/tests/msgs/test_bmc.py000066400000000000000000000115071436677633700203140ustar00rootroot00000000000000#!/usr/bin/env python from array import array import pyipmi.msgs.bmc from pyipmi.msgs import decode_message def test_getdeviceid_decode_req(): m = pyipmi.msgs.bmc.GetDeviceIdReq() decode_message(m, b'') def test_getdeviceid_decode_rsp_with_cc(): m = pyipmi.msgs.bmc.GetDeviceIdRsp() decode_message(m, b'\xc0') assert m.completion_code == 0xc0 def test_getdeviceid_decode_valid_rsp(): m = pyipmi.msgs.bmc.GetDeviceIdRsp() decode_message(m, b'\x00\x0c\x89\x00\x00\x02\x3d\x98' b'\x3a\x00\xbe\x14\x04\x00\x02\x00') assert m.completion_code == 0 assert m.device_id == 0x0c assert m.device_revision.device_revision == 9 assert m.device_revision.provides_device_sdrs == 1 assert m.firmware_revision.major == 0 assert m.firmware_revision.device_available == 0 assert m.firmware_revision.minor == 0 assert m.ipmi_version == 2 assert m.additional_support.sensor == 1 assert m.additional_support.sdr_repository == 0 assert m.additional_support.sel == 1 assert m.additional_support.fru_inventory == 1 assert m.additional_support.ipmb_event_receiver == 1 assert m.additional_support.ipmb_event_generator == 1 assert m.additional_support.bridge == 0 assert m.additional_support.chassis == 0 assert m.manufacturer_id == 15000 assert m.product_id == 5310 assert m.auxiliary == array('B', [4, 0, 2, 0]) def test_getdeviceid_decode_valid_rsp_wo_aux(): m = pyipmi.msgs.bmc.GetDeviceIdRsp() decode_message(m, b'\x00\x0c\x89\x00\x00\x02\x3d\x98' b'\x3a\x00\xbe\x14') assert m.completion_code == 0 assert m.device_id == 0x0c assert m.device_revision.device_revision == 9 assert m.device_revision.provides_device_sdrs == 1 assert m.firmware_revision.major == 0 assert m.firmware_revision.device_available == 0 assert m.firmware_revision.minor == 0 assert m.ipmi_version == 2 assert m.additional_support.sensor == 1 assert m.additional_support.sdr_repository == 0 assert m.additional_support.sel == 1 assert m.additional_support.fru_inventory == 1 assert m.additional_support.ipmb_event_receiver == 1 assert m.additional_support.ipmb_event_generator == 1 assert m.additional_support.bridge == 0 assert m.additional_support.chassis == 0 assert m.manufacturer_id == 15000 assert m.product_id == 5310 def test_getselftestresults_decode_test_passed_rsp(): m = pyipmi.msgs.bmc.GetSelftestResultsRsp() decode_message(m, b'\x00\x55\x00') assert m.completion_code == 0x00 assert m.result == 0x55 assert int(m.status) == 0x00 def test_getselftestresults_decode_test_fail_not_implemented_rsp(): m = pyipmi.msgs.bmc.GetSelftestResultsRsp() decode_message(m, b'\x00\x56\x00') assert m.completion_code == 0x00 assert m.result == 0x56 assert int(m.status) == 0x00 def test_getselftestresults_decode_test_fail_corrupted_sel_rsp(): m = pyipmi.msgs.bmc.GetSelftestResultsRsp() decode_message(m, b'\x00\x57\x80') assert m.completion_code == 0x00 assert m.result == 0x57 assert int(m.status) == 0x80 assert m.status.cannot_access_sel_device == 1 assert m.status.cannot_access_sdr_device == 0 assert m.status.cannot_access_bmc_fru_device == 0 assert m.status.ipmb_signal_lines_do_not_respond == 0 assert m.status.sdr_repository_empty == 0 assert m.status.internal_use_area_corrupted == 0 assert m.status.controller_bootblock_corrupted == 0 assert m.status.controller_firmware_corrupted == 0 def test_getselftestresults_decode_test_fail_corrupted_sdr_rsp(): m = pyipmi.msgs.bmc.GetSelftestResultsRsp() decode_message(m, b'\x00\x57\x40') assert m.completion_code == 0x00 assert m.result == 0x57 assert int(m.status) == 0x40 assert m.status.cannot_access_sel_device == 0 assert m.status.cannot_access_sdr_device == 1 assert m.status.cannot_access_bmc_fru_device == 0 assert m.status.ipmb_signal_lines_do_not_respond == 0 assert m.status.sdr_repository_empty == 0 assert m.status.internal_use_area_corrupted == 0 assert m.status.controller_bootblock_corrupted == 0 assert m.status.controller_firmware_corrupted == 0 def test_getselftestresults_decode_test_fail_corrupted_fru_rsp(): m = pyipmi.msgs.bmc.GetSelftestResultsRsp() decode_message(m, b'\x00\x57\x20') assert m.completion_code == 0x00 assert m.result == 0x57 assert int(m.status) == 0x20 assert m.status.cannot_access_sel_device == 0 assert m.status.cannot_access_sdr_device == 0 assert m.status.cannot_access_bmc_fru_device == 1 assert m.status.ipmb_signal_lines_do_not_respond == 0 assert m.status.sdr_repository_empty == 0 assert m.status.internal_use_area_corrupted == 0 assert m.status.controller_bootblock_corrupted == 0 assert m.status.controller_firmware_corrupted == 0 python-ipmi-0.5.4/tests/msgs/test_chassis.py000066400000000000000000000060241436677633700212060ustar00rootroot00000000000000#!/usr/bin/env python from array import array import pyipmi.msgs.chassis from pyipmi.msgs import encode_message, decode_message def test_getchassisstatus_encode_valid_req(): m = pyipmi.msgs.chassis.GetChassisStatusReq() data = encode_message(m) assert m.__netfn__ == 0 assert m.__cmdid__ == 1 assert data == b'' def test_getchassisstatus_decode_valid_rsp(): m = pyipmi.msgs.chassis.GetChassisStatusRsp() decode_message(m, b'\x00\xea\xaa\xaa') assert m.completion_code == 0x00 assert m.current_power_state.power_on == 0 assert m.current_power_state.power_overload == 1 assert m.current_power_state.interlock == 0 assert m.current_power_state.power_fault == 1 assert m.current_power_state.power_control_fault == 0 assert m.current_power_state.power_restore_policy == 3 assert m.last_power_event.ac_failed == 0 assert m.last_power_event.power_overload == 1 assert m.last_power_event.power_interlock == 0 assert m.last_power_event.power_fault == 1 assert m.last_power_event.power_is_on_via_ipmi_command == 0 assert m.misc_chassis_state.chassis_intrusion_active == 0 assert m.misc_chassis_state.front_panel_lockout_active == 1 assert m.misc_chassis_state.drive_fault == 0 assert m.misc_chassis_state.cooling_fault_detected == 1 def test_getchassisstatus_decode_valid_optional_byte_rsp(): m = pyipmi.msgs.chassis.GetChassisStatusRsp() decode_message(m, b'\x00\x00\x00\00\xaa') assert m.completion_code == 0x00 assert m.front_panel_button_capabilities == 0xaa def test_chassiscontrol_encode_valid_req(): m = pyipmi.msgs.chassis.ChassisControlReq() m.control.option = 1 data = encode_message(m) assert m.__netfn__ == 0 assert m.__cmdid__ == 2 assert data == b'\x01' def test_getsystembootoptions_encode_valid_req(): m = pyipmi.msgs.chassis.GetSystemBootOptionsReq() m.parameter_selector.boot_option_parameter_selector = 5 data = encode_message(m) assert m.__netfn__ == 0 assert m.__cmdid__ == 9 assert data == b'\x05\x00\x00' def test_getsystembootoptions_decode_valid_rsp(): m = pyipmi.msgs.chassis.GetSystemBootOptionsRsp() decode_message(m, b'\x00\x01\x85\x00\x08\x00\x00\x00') assert m.completion_code == 0x00 assert m.parameter_version.parameter_version == 1 assert m.parameter_valid.boot_option_parameter_selector == 5 assert m.parameter_valid.parameter_validity == 1 assert m.data == array('B', b'\x00\x08\x00\x00\x00') def test_setsystembootoptions_encode_valid_req(): m = pyipmi.msgs.chassis.SetSystemBootOptionsReq() m.parameter_selector.boot_option_parameter_selector = 5 m.parameter_selector.parameter_validity = 1 m.data = array('B', b'\x70\x08\x00\x00\x00') data = encode_message(m) assert m.__netfn__ == 0 assert m.__cmdid__ == 8 assert data == b'\x85\x70\x08\x00\x00\x00' def test_setsystembootoptions_decode_valid_rsp(): m = pyipmi.msgs.chassis.SetSystemBootOptionsRsp() decode_message(m, b'\x00') assert m.completion_code == 0x00 python-ipmi-0.5.4/tests/msgs/test_device_messaging.py000066400000000000000000000342771436677633700230600ustar00rootroot00000000000000#!/usr/bin/env python from array import array import pyipmi.msgs.device_messaging from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_setbmcglobalenables_encode_all_disabled_req(): m = pyipmi.msgs.device_messaging.SetBmcGlobalEnablesReq() m.enables.oem_2 = 0 m.enables.oem_1 = 0 m.enables.oem_0 = 0 m.enables.system_event_logging = 0 m.enables.event_message_buffer = 0 m.enables.event_message_buffer_full_interrupt = 0 m.enables.receive_message_queue_interrupt = 0 data = encode_message(m) assert data == b'\x00' def test_setbmcglobalenables_encode_enable_oem_2_req(): m = pyipmi.msgs.device_messaging.SetBmcGlobalEnablesReq() m.enables.oem_2 = 1 m.enables.oem_1 = 0 m.enables.oem_0 = 0 m.enables.system_event_logging = 0 m.enables.event_message_buffer = 0 m.enables.event_message_buffer_full_interrupt = 0 m.enables.receive_message_queue_interrupt = 0 data = encode_message(m) assert data == b'\x80' def test_setbmcglobalenables_encode_enable_oem_1_req(): m = pyipmi.msgs.device_messaging.SetBmcGlobalEnablesReq() m.enables.oem_2 = 0 m.enables.oem_1 = 1 m.enables.oem_0 = 0 m.enables.system_event_logging = 0 m.enables.event_message_buffer = 0 m.enables.event_message_buffer_full_interrupt = 0 m.enables.receive_message_queue_interrupt = 0 data = encode_message(m) assert data == b'\x40' def test_setbmcglobalenables_encode_enable_oem_0_req(): m = pyipmi.msgs.device_messaging.SetBmcGlobalEnablesReq() m.enables.oem_2 = 0 m.enables.oem_1 = 0 m.enables.oem_0 = 1 m.enables.system_event_logging = 0 m.enables.event_message_buffer = 0 m.enables.event_message_buffer_full_interrupt = 0 m.enables.receive_message_queue_interrupt = 0 data = encode_message(m) assert data == b'\x20' def test_setbmcglobalenables_encode_enable_receive_queue_interrupt_req(): m = pyipmi.msgs.device_messaging.SetBmcGlobalEnablesReq() m.enables.oem_2 = 0 m.enables.oem_1 = 0 m.enables.oem_0 = 0 m.enables.system_event_logging = 0 m.enables.event_message_buffer = 0 m.enables.event_message_buffer_full_interrupt = 0 m.enables.receive_message_queue_interrupt = 1 data = encode_message(m) assert data == b'\x01' def test_getbmcglobalenables_decode_all_disabled_rsp(): m = pyipmi.msgs.device_messaging.GetBmcGlobalEnablesRsp() decode_message(m, b'\x00\x00') assert m.completion_code == 0x00 assert m.enables.oem_2 == 0 assert m.enables.oem_1 == 0 assert m.enables.oem_0 == 0 assert m.enables.system_event_logging == 0 assert m.enables.event_message_buffer == 0 assert m.enables.event_message_buffer_full_interrupt == 0 assert m.enables.receive_message_queue_interrupt == 0 def test_getbmcglobalenables_decode_oem_2_enabled_rsp(): m = pyipmi.msgs.device_messaging.GetBmcGlobalEnablesRsp() decode_message(m, b'\x00\x80') assert m.completion_code == 0x00 assert m.enables.oem_2 == 1 assert m.enables.oem_1 == 0 assert m.enables.oem_0 == 0 assert m.enables.system_event_logging == 0 assert m.enables.event_message_buffer == 0 assert m.enables.event_message_buffer_full_interrupt == 0 assert m.enables.receive_message_queue_interrupt == 0 def test_getbmcglobalenables_decode_oem_0_enabled_rsp(): m = pyipmi.msgs.device_messaging.GetBmcGlobalEnablesRsp() decode_message(m, b'\x00\x20') assert m.completion_code == 0x00 assert m.enables.oem_2 == 0 assert m.enables.oem_1 == 0 assert m.enables.oem_0 == 1 assert m.enables.system_event_logging == 0 assert m.enables.event_message_buffer == 0 assert m.enables.event_message_buffer_full_interrupt == 0 assert m.enables.receive_message_queue_interrupt == 0 def test_clearmessageflags_encode_clear_none_req(): m = pyipmi.msgs.device_messaging.ClearMessageFlagsReq() m.clear.oem_2 = 0 m.clear.oem_1 = 0 m.clear.oem_0 = 0 m.clear.watchdog_pretimeout_interrupt_flag = 0 m.clear.event_message_buffer = 0 m.clear.receive_message_queue = 0 data = encode_message(m) assert data == b'\x00' def test_clearmessageflags_encode_clear_oem_2_req(): m = pyipmi.msgs.device_messaging.ClearMessageFlagsReq() m.clear.oem_2 = 1 m.clear.oem_1 = 0 m.clear.oem_0 = 0 m.clear.watchdog_pretimeout_interrupt_flag = 0 m.clear.event_message_buffer = 0 m.clear.receive_message_queue = 0 data = encode_message(m) assert data == b'\x80' def test_clearmessageflags_encode_clear_oem_0_req(): m = pyipmi.msgs.device_messaging.ClearMessageFlagsReq() m.clear.oem_2 = 0 m.clear.oem_1 = 0 m.clear.oem_0 = 1 m.clear.watchdog_pretimeout_interrupt_flag = 0 m.clear.event_message_buffer = 0 m.clear.receive_message_queue = 0 data = encode_message(m) assert data == b'\x20' def test_clearmessageflags_encode_clear_receive_message_queue_req(): m = pyipmi.msgs.device_messaging.ClearMessageFlagsReq() m.clear.oem_2 = 0 m.clear.oem_1 = 0 m.clear.oem_0 = 0 m.clear.watchdog_pretimeout_interrupt_flag = 0 m.clear.event_message_buffer = 0 m.clear.receive_message_queue = 1 data = encode_message(m) assert data == b'\x01' def test_getmessageflags_decode_not_flag_set_rsp(): m = pyipmi.msgs.device_messaging.GetMessageFlagsRsp() decode_message(m, b'\x00\x00') assert m.completion_code == 0x00 assert m.flag.oem_2 == 0 assert m.flag.oem_1 == 0 assert m.flag.oem_0 == 0 assert m.flag.watchdog_pretimeout_interrupt_occurred == 0 assert m.flag.event_message_buffer_full == 0 assert m.flag.receive_message_available == 0 def test_getmessageflags_decode_oem_2_set_rsp(): m = pyipmi.msgs.device_messaging.GetMessageFlagsRsp() decode_message(m, b'\x00\x80') assert m.completion_code == 0x00 assert m.flag.oem_2 == 1 assert m.flag.oem_1 == 0 assert m.flag.oem_0 == 0 assert m.flag.watchdog_pretimeout_interrupt_occurred == 0 assert m.flag.event_message_buffer_full == 0 assert m.flag.receive_message_available == 0 def test_getmessageflags_decode_event_message_full_set_rsp(): m = pyipmi.msgs.device_messaging.GetMessageFlagsRsp() decode_message(m, b'\x00\x02') assert m.completion_code == 0x00 assert m.flag.oem_2 == 0 assert m.flag.oem_1 == 0 assert m.flag.oem_0 == 0 assert m.flag.watchdog_pretimeout_interrupt_occurred == 0 assert m.flag.event_message_buffer_full == 1 assert m.flag.receive_message_available == 0 def test_enablemessagechannelreceive_encode_all_off_req(): m = pyipmi.msgs.device_messaging.EnableMessageChannelReceiveReq() m.channel.number = 0 m.channel.state = 0 data = encode_message(m) assert data == b'\x00\x00' def test_enablemessagechannelreceive_encode_channel1_enable_req(): m = pyipmi.msgs.device_messaging.EnableMessageChannelReceiveReq() m.channel.number = 1 m.channel.state = 1 data = encode_message(m) assert data == b'\x01\x01' def test_enablemessagechannelreceive_encode_channel2_enable_req(): m = pyipmi.msgs.device_messaging.EnableMessageChannelReceiveReq() m.channel.number = 2 m.channel.state = 1 data = encode_message(m) assert data == b'\x02\x01' def test_enablemessagechannelreceive_decode_channel1_enabled_rsp(): m = pyipmi.msgs.device_messaging.EnableMessageChannelReceiveRsp() decode_message(m, b'\x00\x01\x01') assert m.completion_code == 0x00 assert m.channel.number == 1 assert m.channel.state == 1 def test_getmessage_decode_no_data_rsp(): m = pyipmi.msgs.device_messaging.GetMessageRsp() decode_message(m, b'\x00\x21') assert m.completion_code == 0x00 assert m.channel.number == 1 assert m.channel.privilege_level == 2 def test_getmessage_decode_with_data_rsp(): m = pyipmi.msgs.device_messaging.GetMessageRsp() decode_message(m, b'\x00\x21\xaa\xff\xff\xee') assert m.completion_code == 0x00 assert m.channel.number == 1 assert m.channel.privilege_level == 2 assert m.data == array('B', [0xaa, 0xff, 0xff, 0xee]) def test_readeventmessagebuffer_decode_rsp(): m = pyipmi.msgs.device_messaging.ReadEventMessageBufferRsp() decode_message(m, b'\x00\x00\x01\x02\x03\x04\x05\x06\x07' b'\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f') assert m.completion_code == 0x00 assert m.event_data == array('B', b'\x00\x01\x02\x03\x04\x05\x06\x07' b'\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f') def test_masterwriteread_encode_req_all_zero_read(): m = pyipmi.msgs.device_messaging.MasterWriteReadReq() m.bus_id.type = 0 m.bus_id.id = 0 m.bus_id.channel = 0 m.bus_id.slave_address = 0 m.read_count = 0 data = encode_message(m) assert data == b'\x00\x00\x00' def test_masterwriteread_encode_req_for_read(): m = pyipmi.msgs.device_messaging.MasterWriteReadReq() m.bus_id.type = 1 m.bus_id.id = 2 m.bus_id.channel = 4 m.bus_id.slave_address = 0x3a m.read_count = 5 data = encode_message(m) assert data == b'\x45\x74\x05' def test_masterwriteread_encode_req_for_write(): m = pyipmi.msgs.device_messaging.MasterWriteReadReq() m.bus_id.type = 0 m.bus_id.id = 0 m.bus_id.channel = 0 m.bus_id.slave_address = 0 m.read_count = 0 m.data = [1, 0x23, 0x45] data = encode_message(m) assert data == b'\x00\x00\x00\x01\x23\x45' def test_masterwriteread_decode_rsp(): m = pyipmi.msgs.device_messaging.MasterWriteReadRsp() decode_message(m, b'\x00\x11\x22\x33\x44') assert m.completion_code == 0x00 assert m.data == array('B', [0x11, 0x22, 0x33, 0x44]) def test_get_channel_authentication_capabilities_req(): m = pyipmi.msgs.device_messaging.GetChannelAuthenticationCapabilitiesReq() m.channel.number = 6 m.channel.type = 1 m.privilege_level.requested = 5 data = encode_message(m) assert m.cmdid == 0x38 assert m.netfn == 6 assert data == b'\x86\x05' def test_get_channel_authentication_capabilities_rsp(): m = pyipmi.msgs.device_messaging.GetChannelAuthenticationCapabilitiesRsp() decode_message(m, b'\x00\x01\x15\x19\x44\x55\x66\x77\x88') assert m.cmdid == 0x38 assert m.netfn == 7 assert m.completion_code == 0x00 assert m.channel_number == 1 assert m.support.none == 1 assert m.support.md2 == 0 assert m.support.md5 == 1 assert m.support.straight == 1 assert m.support.oem_proprietary == 0 assert m.status.anonymous_login_enabled == 1 assert m.status.anonymous_login_null_user == 0 assert m.status.anonymous_login_non_null == 0 assert m.status.user_level == 1 assert m.status.per_message == 1 assert m.status.kg == 0 def test_get_session_challenge_rsp(): m = pyipmi.msgs.device_messaging.GetSessionChallengeRsp() data = encode_message(m) assert data == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' def test_get_session_challenge_rsp_cc_not_ok(): m = pyipmi.msgs.device_messaging.GetSessionChallengeRsp() m.completion_code = 0xc1 m.temporary_session_id = 0x11121314 m.challenge_string = '0123456789abcdef' data = encode_message(m) assert data == b'\xc1\x14\x13\x12\x110123456789abcdef' def test_get_session_challenge_req(): m = pyipmi.msgs.device_messaging.GetSessionChallengeReq() m.authentication.type = 1 # m.user_name = "helloworld" data = encode_message(m) assert m.cmdid == 0x39 assert m.netfn == 6 assert data == \ b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' m.authentication.type = 1 m.user_name = '0123456789abcdef' data = encode_message(m) assert m.cmdid == 0x39 assert m.netfn == 6 assert data == b'\x010123456789abcdef' def test_get_username_rsp(): m = pyipmi.msgs.device_messaging.GetUserNameRsp() decode_message(m, b'\x00\x72\x6f\x6f\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert m.cmdid == 0x46 assert m.netfn == 7 assert m.completion_code == 0x00 assert m.user_name == b'root\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' def test_get_username_req(): m = pyipmi.msgs.device_messaging.GetUserNameReq() m.userid.userid = 2 data = encode_message(m) assert data == b'\x02' assert m.cmdid == 0x46 assert m.netfn == 6 assert m.userid.userid == 2 def test_set_user_password_req(): m = pyipmi.msgs.device_messaging.SetUserPasswordReq() m.userid.userid = 2 m.operation.operation = 0b10 m.password = "password".ljust(16, '\x00') data = encode_message(m) assert m.cmdid == 0x47 assert m.netfn == 6 assert data == b'\x02\x02password\x00\x00\x00\x00\x00\x00\x00\x00' def test_set_user_password_rsp(): m = pyipmi.msgs.device_messaging.SetUserPasswordRsp() decode_message(m, b'\x00') assert m.cmdid == 0x47 assert m.netfn == 7 assert m.completion_code == 0x00 def test_get_user_access_req(): m = pyipmi.msgs.device_messaging.GetUserAccessReq() m.channel.channel_number = 1 m.userid.userid = 2 data = encode_message(m) assert m.cmdid == 0x44 assert m.netfn == 6 assert data == b'\x01\x02' def test_get_user_access_rsp(): m = pyipmi.msgs.device_messaging.GetUserAccessRsp() decode_message(m, b'\x00\x0a\x42\x01\x13') assert m.cmdid == 0x44 assert m.netfn == 7 assert m.max_user.max_user == 10 assert m.enabled_user.count == 2 assert m.enabled_user.status == 1 assert m.fixed_names.count == 1 assert m.channel_access.privilege == 3 assert m.channel_access.ipmi_msg == 1 assert m.channel_access.link_auth == 0 assert m.channel_access.callback == 0 def test_set_user_access_req(): m = pyipmi.msgs.device_messaging.SetUserAccessReq() m.channel_access.channel_number = 1 m.channel_access.ipmi_msg = 1 m.channel_access.link_auth = 0 m.channel_access.callback = 0 m.channel_access.enable_change = 1 m.userid.userid = 2 m.privilege.privilege_level = 3 data = encode_message(m) assert m.cmdid == 0x43 assert m.netfn == 6 assert data == b'\x91\x02\x03' def test_user_access_rsp(): m = pyipmi.msgs.device_messaging.SetUserAccessRsp() decode_message(m, b'\x00') assert m.cmdid == 0x43 assert m.netfn == 7 assert m.completion_code == 0x00 python-ipmi-0.5.4/tests/msgs/test_event.py000066400000000000000000000021511436677633700206670ustar00rootroot00000000000000#!/usr/bin/env python import pyipmi.msgs.event from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_seteventreceiver_encode_lun0_req(): m = pyipmi.msgs.event.SetEventReceiverReq() m.event_receiver.ipmb_i2c_slave_address = 0x10 m.event_receiver.lun = 0 data = encode_message(m) assert data == b'\x20\x00' def test_seteventreceiver_encode_lun3_req(): m = pyipmi.msgs.event.SetEventReceiverReq() m.event_receiver.ipmb_i2c_slave_address = 0x10 m.event_receiver.lun = 3 data = encode_message(m) assert data, b'\x20\x03' def test_geteventreceiver_decode_lun0_rsp(): m = pyipmi.msgs.event.GetEventReceiverRsp() decode_message(m, b'\x00\x20\x00') assert m.completion_code == 0x00 assert m.event_receiver.ipmb_i2c_slave_address == 0x10 assert m.event_receiver.lun == 0 def test_geteventreceiver_decode_lun3_rsp(): m = pyipmi.msgs.event.GetEventReceiverRsp() decode_message(m, b'\x00\x20\x03') assert m.completion_code == 0x00 assert m.event_receiver.ipmb_i2c_slave_address == 0x10 assert m.event_receiver.lun == 3 python-ipmi-0.5.4/tests/msgs/test_fru.py000066400000000000000000000064461436677633700203550ustar00rootroot00000000000000#!/usr/bin/env python import pytest from array import array import pyipmi from pyipmi.errors import DecodingError, EncodingError from pyipmi.msgs import encode_message, decode_message def test_fruinventoryareainfo_decode_valid_rsp(): m = pyipmi.msgs.fru.GetFruInventoryAreaInfoRsp() decode_message(m, b'\x00\x01\x02\x01') assert m.completion_code == 0x00 assert m.area_size == 0x0201 assert m.area_info.access == 1 def test_writefrudatareq_decode_valid_req(): m = pyipmi.msgs.fru.WriteFruDataReq() decode_message(m, b'\x01\x02\x03\x04\x05') assert m.fru_id == 1 assert m.offset == 0x302 assert m.data == array('B', b'\x04\x05') def test_writefrudatareq_encode_valid_req(): m = pyipmi.msgs.fru.WriteFruDataReq() m.fru_id = 1 m.offset = 0x302 m.data = array('B', b'\x04\x05') data = encode_message(m) assert data == b'\x01\x02\x03\x04\x05' def test_writefrudatareq_decode_valid_req_wo_data(): m = pyipmi.msgs.fru.WriteFruDataReq() decode_message(m, b'\x01\x02\x03') assert m.fru_id == 1 assert m.offset == 0x302 assert m.data == array('B') def test_writefrudatareq_encode_valid_req_wo_data(): m = pyipmi.msgs.fru.WriteFruDataReq() m.fru_id = 1 m.offset = 0x302 m.data = array('B') data = encode_message(m) assert data == b'\x01\x02\x03' def test_writefrudatareq_decode_invalid_req(): m = pyipmi.msgs.fru.WriteFruDataReq() with pytest.raises(DecodingError): decode_message(m, b'\x01\x02') def test_readfrudatareq_decode_valid_req(): m = pyipmi.msgs.fru.ReadFruDataReq() decode_message(m, b'\x01\x02\x03\x04') assert m.fru_id == 1 assert m.offset == 0x302 assert m.count == 4 def test_readfrudatareq_decode_short_req(): m = pyipmi.msgs.fru.ReadFruDataReq() with pytest.raises(DecodingError): decode_message(m, b'\x01\x02\x03') def test_readfrudatareq_decode_long_req(): m = pyipmi.msgs.fru.ReadFruDataReq() with pytest.raises(DecodingError): decode_message(m, b'\x01\x02\x03\04\x05') def test_readfrudatareq_encode_valid_req(): m = pyipmi.msgs.fru.ReadFruDataReq() m.fru_id = 1 m.offset = 0x302 m.count = 4 data = encode_message(m) assert data == b'\x01\x02\x03\x04' def test_readfrudatarsp_decode_valid_rsp(): m = pyipmi.msgs.fru.ReadFruDataRsp() decode_message(m, b'\x00\x05\x01\x02\x03\x04\x05') assert m.completion_code == 0 assert m.count == 5 assert m.data == array('B', b'\x01\x02\x03\x04\x05') def test_readfrudatarsp_decode_rsp_with_cc(): m = pyipmi.msgs.fru.ReadFruDataRsp() decode_message(m, b'\xc0') assert m.completion_code == 0xc0 def test_readfrudatarsp_decode_invalid_rsp(): m = pyipmi.msgs.fru.ReadFruDataRsp() with pytest.raises(DecodingError): decode_message(m, b'\x00\x01\x01\x02') def test_readfrudatarsp_encode_valid_rsp(): m = pyipmi.msgs.fru.ReadFruDataRsp() m.completion_code = 0 m.count = 5 m.data = array('B', b'\x01\x02\x03\x04\x05') data = encode_message(m) assert data == b'\x00\x05\x01\x02\x03\x04\x05' def test_readfrudatarsp_encode_invalid_rsp(): m = pyipmi.msgs.fru.ReadFruDataRsp() m.completion_code = 0 m.count = 1 m.data = array('B', b'\x01\x02') with pytest.raises(EncodingError): encode_message(m) python-ipmi-0.5.4/tests/msgs/test_hpm.py000066400000000000000000000023031436677633700203310ustar00rootroot00000000000000#!/usr/bin/env python import pyipmi.msgs.hpm from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_uploadfirmwareblockreq_encode(): m = pyipmi.msgs.hpm.UploadFirmwareBlockReq() m.number = 1 m.data = [0, 1, 2, 3] data = encode_message(m) assert data == b'\x00\x01\x00\x01\x02\x03' def test_activatefirmwarereq_decode_valid_req(): m = pyipmi.msgs.hpm.ActivateFirmwareReq() decode_message(m, b'\x00\x01') assert m.picmg_identifier == 0 assert m.rollback_override_policy == 1 def test_activatefirmwarereq_encode_valid_req(): m = pyipmi.msgs.hpm.ActivateFirmwareReq() m.picmg_identifier = 0 m.rollback_override_policy = 0x1 data = encode_message(m) assert data == b'\x00\x01' def test_activatefirmwarereq_decode_valid_req_wo_optional(): m = pyipmi.msgs.hpm.ActivateFirmwareReq() decode_message(m, b'\x00') assert m.picmg_identifier == 0 assert m.rollback_override_policy is None def test_activatefirmwarereq_encode_valid_req_wo_optional(): m = pyipmi.msgs.hpm.ActivateFirmwareReq() m.picmg_identifier = 0 m.rollback_override_policy = None data = encode_message(m) assert data == b'\x00' python-ipmi-0.5.4/tests/msgs/test_message.py000066400000000000000000000040311436677633700211710ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from array import array from pyipmi.utils import ByteBuffer from pyipmi.msgs.message import (Bitfield, Message, UnsignedInt, RemainingBytes, String) class TMessage(object): def __init__(self, field): setattr(self, field.name, field.create()) self.field = field def encode(self): data = ByteBuffer() self.field.encode(self, data) return data def decode(self, data): data = ByteBuffer(data) self.field.decode(self, data) def test_bitfield_encode(): t = TMessage(Bitfield('status', 1, Bitfield.Bit('erase_in_progress', 4), Bitfield.ReservedBit(4, 0),)) t.status.erase_in_progress = 1 byte_buffer = t.encode() assert byte_buffer.array == array('B', [0x1]) def test_unsignedint_encode(): t = TMessage(UnsignedInt('test', 4)) t.test = 0x12345678 byte_buffer = t.encode() assert byte_buffer.array == array('B', [0x78, 0x56, 0x34, 0x12]) t = TMessage(UnsignedInt('test', 8)) t.test = 0x12345678 byte_buffer = t.encode() assert byte_buffer.array == array('B', [0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0]) def test_unsignedint_decode(): t = TMessage(UnsignedInt('test', 1)) t.decode(b'\x12') assert t.test == 0x12 t.decode(b'\xd7') assert t.test == 0xd7 def test_string_encode(): t = TMessage(String('test', 10)) t.test = '1234' byte_buffer = t.encode() assert byte_buffer.array == array('B', [0x31, 0x32, 0x33, 0x34]) def test_string_decode(): t = TMessage(String('test', 10)) t.decode(b'abcdef') assert t.test == b'abcdef' def test_remainingbytes_encode(): t = TMessage(RemainingBytes('test')) t.test = [0xb4, 1] byte_buffer = t.encode() assert byte_buffer.array == array('B', [0xb4, 0x01]) def test_message(): msg = Message() msg.__netfn__ = 0x1 msg.__cmdid__ = 0x2 assert msg.lun == 0 assert msg.netfn == 1 assert msg.cmdid == 2 python-ipmi-0.5.4/tests/msgs/test_picmg.py000066400000000000000000000135641436677633700206570ustar00rootroot00000000000000#!/usr/bin/env python import pyipmi.msgs.bmc import pyipmi.msgs.sel import pyipmi.msgs.event import pyipmi.msgs.hpm import pyipmi.msgs.sensor from pyipmi.msgs import constants, decode_message, encode_message from pyipmi.msgs.picmg import PICMG_IDENTIFIER def test_get_picmg_properties_req(): msg = pyipmi.msgs.picmg.GetPicmgPropertiesReq() assert msg.cmdid == constants.CMDID_GET_PICMG_PROPERTIES assert msg.netfn == constants.NETFN_GROUP_EXTENSION assert msg.group_extension == PICMG_IDENTIFIER def test_get_address_info_picmg_2_9_rsp(): m = pyipmi.msgs.picmg.GetAddressInfoRsp() decode_message(m, b'\x00\x00\x01\x02\x03') assert m.completion_code == 0x00 assert m.picmg_identifier == 0x00 assert m.hardware_address == 0x01 assert m.ipmb_0_address == 0x02 assert m.ipmb_1_address == 0x03 assert m.fru_id is None assert m.site_id is None assert m.site_type is None assert m.carrier_number is None def test_get_address_info_picmg_3_0_rsp(): m = pyipmi.msgs.picmg.GetAddressInfoRsp() decode_message(m, b'\x00\x00\x01\x02\xff\x04\x05\x06') assert m.completion_code == 0x00 assert m.picmg_identifier == 0x00 assert m.hardware_address == 0x01 assert m.ipmb_0_address == 0x02 assert m.ipmb_1_address == 0xff assert m.fru_id == 0x04 assert m.site_id == 0x05 assert m.site_type == 0x06 assert m.carrier_number is None def test_get_address_info_mtca_rsp(): m = pyipmi.msgs.picmg.GetAddressInfoRsp() decode_message(m, b'\x00\x00\x01\x02\x03\x04\x05\x06\x07') assert m.completion_code == 0x00 assert m.picmg_identifier == 0x00 assert m.hardware_address == 0x01 assert m.ipmb_0_address == 0x02 assert m.ipmb_1_address == 0x03 assert m.fru_id == 0x04 assert m.site_id == 0x05 assert m.site_type == 0x06 assert m.carrier_number == 0x07 def test_get_shelf_address_info_rsp(): m = pyipmi.msgs.picmg.GetShelfAddressInfoRsp() decode_message(m, b'\x00\x00\x01\x02') assert m.completion_code == 0x00 assert m.picmg_identifier == 0x00 assert m.shelf_address[0] == 0x01 assert m.shelf_address[1] == 0x02 def test_encode_fru_control_req(): m = pyipmi.msgs.picmg.FruControlReq() m.fru_id = 1 m.option = 2 data = encode_message(m) assert data == b'\x00\x01\x02' def test_decode_fru_control_rsp(): m = pyipmi.msgs.picmg.FruControlRsp() decode_message(m, b'\x00\x00\xaa') assert m.rsp_data[0] == 0xaa def test_clear_activation_lock_req(): m = pyipmi.msgs.picmg.SetFruActivationPolicyReq() m.fru_id = 1 m.mask.activation_locked = 1 m.set.activation_locked = 0 data = encode_message(m) assert data == b'\x00\x01\x01\x00' def test_set_activation_lock_req(): m = pyipmi.msgs.picmg.SetFruActivationPolicyReq() m.fru_id = 1 m.mask.activation_locked = 1 m.set.activation_locked = 1 data = encode_message(m) assert data == b'\x00\x01\x01\x01' def test_clear_deactivation_lock_req(): m = pyipmi.msgs.picmg.SetFruActivationPolicyReq() m.fru_id = 1 m.mask.deactivation_locked = 1 m.set.deactivation_locked = 0 data = encode_message(m) assert data == b'\x00\x01\x02\x00' def test_set_deactivation_lock_req(): m = pyipmi.msgs.picmg.SetFruActivationPolicyReq() m.fru_id = 1 m.mask.deactivation_locked = 1 m.set.deactivation_locked = 1 data = encode_message(m) assert data == b'\x00\x01\x02\x02' def test_decode_rsp_local_control_state(): m = pyipmi.msgs.picmg.GetFruLedStateRsp() decode_message(m, b'\x00\x00\x01\xff\x00\x02') assert m.completion_code == 0x00 assert m.led_states.local_avail == 1 assert m.local_function == 0xff assert m.local_on_duration == 0 assert m.local_color == 2 def test_decode_rsp_override_mode(): m = pyipmi.msgs.picmg.GetFruLedStateRsp() decode_message(m, b'\x00\x00\x03\xff\x00\x03\xff\x00\x03') assert m.completion_code == 0x00 assert m.led_states.local_avail == 1 assert m.local_function == 0xff assert m.local_on_duration == 0 assert m.local_color == 3 assert m.led_states.override_en == 1 assert m.override_function == 0xff assert m.override_on_duration == 0 assert m.override_color == 3 assert m.led_states.lamp_test_en == 0 def test_decode_rsp_lamp_test_and_override_mode(): m = pyipmi.msgs.picmg.GetFruLedStateRsp() decode_message(m, b'\x00\x00\x07\xff\x00\x02\xff\x00\x02\x7f') assert m.completion_code == 0x00 assert m.led_states.local_avail == 1 assert m.local_function == 0xff assert m.local_on_duration == 0 assert m.local_color == 2 assert m.led_states.override_en == 1 assert m.override_function == 0xff assert m.override_on_duration == 0 assert m.override_color == 2 assert m.led_states.lamp_test_en == 1 assert m.lamp_test_duration == 0x7f def test_decode_rsp_only_lamp_test_mode(): m = pyipmi.msgs.picmg.GetFruLedStateRsp() decode_message(m, b'\x00\x00\x04\xff\x00\x02\xff\x00\x02\x7f') assert m.completion_code == 0x00 assert m.led_states.local_avail == 0 assert m.local_function == 0xff assert m.local_on_duration == 0 assert m.local_color == 2 assert m.led_states.override_en == 0 assert m.override_function == 0xff assert m.override_on_duration == 0 assert m.override_color == 2 assert m.led_states.lamp_test_en == 1 assert m.lamp_test_duration == 0x7f def test_encode_req_pm_heartbeat(): m = pyipmi.msgs.picmg.SendPmHeartbeatReq() m.timeout = 10 m.ps1.mch_1 = 1 data = encode_message(m) assert data == b'\x00\n\x01' m = pyipmi.msgs.picmg.SendPmHeartbeatReq() m.timeout = 10 m.ps1.mch_2 = 1 data = encode_message(m) assert data == b'\x00\n\x02' def test_decode_rsp_pm_heartbeat(): m = pyipmi.msgs.picmg.SendPmHeartbeatRsp() decode_message(m, b'\x00\x00') assert m.completion_code == 0x00 assert m.picmg_identifier == 0x00 python-ipmi-0.5.4/tests/msgs/test_registry.py000066400000000000000000000014331436677633700214200ustar00rootroot00000000000000 import pyipmi.msgs from pyipmi.msgs import create_message, create_response_message, create_response_by_name, create_request_by_name def test_create_message(): req = create_message(6, 1, None) assert type(req) == pyipmi.msgs.bmc.GetDeviceIdReq req = create_message(7, 1, None) assert type(req) == pyipmi.msgs.bmc.GetDeviceIdRsp def test_create_response(): req = pyipmi.msgs.bmc.GetDeviceIdReq() rsp = create_response_message(req) assert type(rsp) == pyipmi.msgs.bmc.GetDeviceIdRsp def test_create_request_by_name(): req = create_request_by_name('GetDeviceId') assert type(req) == pyipmi.msgs.bmc.GetDeviceIdReq def test_create_response_by_name(): rsp = create_response_by_name('GetDeviceId') assert type(rsp) == pyipmi.msgs.bmc.GetDeviceIdRsp python-ipmi-0.5.4/tests/msgs/test_sdr.py000066400000000000000000000077031436677633700203460ustar00rootroot00000000000000#!/usr/bin/env python from array import array import pyipmi.msgs.sdr from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_getsdrrepositoryinfo_encode_req(): m = pyipmi.msgs.sdr.GetSdrRepositoryInfoReq() data = encode_message(m) assert data == b'' def test_getsdrrepositoryinfo_decode_rsp(): m = pyipmi.msgs.sdr.GetSdrRepositoryInfoRsp() decode_message(m, b'\x00\x51\x00\x11\x55\xaa\x11\x22\x33\x44\x55\x66\x77\x88\xaa') assert m.completion_code == 0x00 assert m.sdr_version == 0x51 assert m.record_count == 0x1100 assert m.free_space == 0xaa55 assert m.most_recent_addition == 0x44332211 assert m.most_recent_erase == 0x88776655 assert m.support.get_allocation_info == 0 assert m.support.reserve == 1 assert m.support.partial_add == 0 assert m.support.delete == 1 assert m.support.update_type == 1 assert m.support.overflow_flag == 1 def test_getsdrrepositoryallocationinfo_encode_req(): m = pyipmi.msgs.sdr.GetSdrRepositoryAllocationInfoReq() data = encode_message(m) assert data == b'' def test_getsdrrepositoryallocationinfo_decode_rsp(): m = pyipmi.msgs.sdr.GetSdrRepositoryAllocationInfoRsp() decode_message(m, b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\xaa') assert m.completion_code == 0x00 assert m.number_of_units == 0x2211 assert m.unit_size == 0x4433 assert m.free_units == 0x6655 assert m.largest_free_block == 0x8877 assert m.maximum_record_size == 0xaa def test_reservesdrrepository_encode_req(): m = pyipmi.msgs.sdr.ReserveSdrRepositoryReq() data = encode_message(m) assert data == b'' def test_reservesdrrepository_decode_rsp(): m = pyipmi.msgs.sdr.ReserveSdrRepositoryRsp() decode_message(m, b'\x00\x11\x22') assert m.completion_code == 0x00 assert m.reservation_id == 0x2211 def test_getsdr_encode_req(): m = pyipmi.msgs.sdr.GetSdrReq() m.reservation_id = 0x1122 m.record_id = 0x3344 m.offset = 0xaa m.bytes_to_read = 0x55 data = encode_message(m) assert data == b'\x22\x11\x44\x33\xaa\x55' def test_getsdr_decode_rsp(): m = pyipmi.msgs.sdr.GetSdrRsp() decode_message(m, b'\x00\x11\x22\x33\x44\x55\x66') assert m.completion_code == 0x00 assert m.next_record_id == 0x2211 assert m.record_data == array('B', [0x33, 0x44, 0x55, 0x66]) def test_addsdr_encode_req(): m = pyipmi.msgs.sdr.AddSdrReq() m.record_data = array('B', [0x55, 0x44]) data = encode_message(m) assert data == b'\x55\x44' def test_addsdr_decode_rsp(): m = pyipmi.msgs.sdr.AddSdrRsp() decode_message(m, b'\x00\x11\x22') assert m.completion_code == 0x00 assert m.record_id == 0x2211 def test_partialaddsdr_encode_req(): m = pyipmi.msgs.sdr.PartialAddSdrReq() m.reservation_id = 0x2211 m.record_id = 0x4433 m.offset = 0xaa m.status.in_progress = 0xaa m.record_data = array('B', [0x55, 0x44]) data = encode_message(m) assert data == b'\x11\x22\x33\x44\xaa\x0a\x55\x44' def test_partialaddsdr_decode_rsp(): m = pyipmi.msgs.sdr.PartialAddSdrRsp() decode_message(m, b'\x00\x11\x22') assert m.completion_code == 0x00 assert m.record_id == 0x2211 def test_deletesdr_encode_req(): m = pyipmi.msgs.sdr.DeleteSdrReq() m.reservation_id = 0x2211 m.record_id = 0x4433 data = encode_message(m) assert data == b'\x11\x22\x33\x44' def test_deletesdr_decode_rsp(): m = pyipmi.msgs.sdr.DeleteSdrRsp() decode_message(m, b'\x00\x11\x22') assert m.completion_code == 0x00 assert m.record_id == 0x2211 def test_clearsdrrepository_encode_req(): m = pyipmi.msgs.sdr.ClearSdrRepositoryReq() m.reservation_id = 0x2211 m.cmd = 1 data = encode_message(m) assert data == b'\x11"CLR\x01' def test_clearsdrrepository_decode_rsp(): m = pyipmi.msgs.sdr.ClearSdrRepositoryRsp() decode_message(m, b'\x00\x11') assert m.completion_code == 0x00 assert m.status.erase_in_progress == 0x1 python-ipmi-0.5.4/tests/msgs/test_sel.py000066400000000000000000000020461436677633700203340ustar00rootroot00000000000000#!/usr/bin/env python import pytest from array import array import pyipmi.msgs.sel from pyipmi.errors import DecodingError from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_getselentry_decode_rsp_with_cc(): m = pyipmi.msgs.sel.GetSelEntryRsp() decode_message(m, b'\xc0') assert m.completion_code == 0xc0 def test_getselentry_decode_invalid_rsp(): m = pyipmi.msgs.sel.GetSelEntryRsp() with pytest.raises(DecodingError): decode_message(m, b'\x00\x01') def test_getselentry_decode_valid_rsp(): m = pyipmi.msgs.sel.GetSelEntryRsp() decode_message(m, b'\x00\x02\x01\x01\x02\x03\x04') assert m.completion_code == 0x00 assert m.next_record_id == 0x0102 assert m.record_data == array('B', [1, 2, 3, 4]) def test_getselentry_encode_valid_rsp(): m = pyipmi.msgs.sel.GetSelEntryRsp() m.completion_code = 0 m.next_record_id = 0x0102 m.record_data = array('B', b'\x01\x02\x03\x04') data = encode_message(m) assert data == b'\x00\x02\x01\x01\x02\x03\x04' python-ipmi-0.5.4/tests/msgs/test_sensor.py000066400000000000000000000200241436677633700210560ustar00rootroot00000000000000#!/usr/bin/env python from array import array import pyipmi.msgs.sensor from pyipmi.msgs import encode_message from pyipmi.msgs import decode_message def test_getdevicesdrinfo_encode_req(): m = pyipmi.msgs.sensor.GetDeviceSdrInfoReq() data = encode_message(m) assert data == b'' def test_getdevicesdrinfo_encode_rsp(): m = pyipmi.msgs.sensor.GetDeviceSdrInfoRsp() decode_message(m, b'\x00\x03\x05') assert m.completion_code == 0x00 assert m.number_of_sensors == 3 assert m.flags.lun0_has_sensors == 1 assert m.flags.lun1_has_sensors == 0 assert m.flags.lun2_has_sensors == 1 assert m.flags.lun3_has_sensors == 0 assert m.flags.dynamic_population == 0 def test_getdevicesdrinfo_encode_rsp_with_timestamp(): m = pyipmi.msgs.sensor.GetDeviceSdrInfoRsp() decode_message(m, b'\x00\x12\x01\xaa\xbb\xcc\xdd') assert m.completion_code == 0x00 assert m.number_of_sensors == 0x12 assert m.flags.lun0_has_sensors == 1 assert m.flags.lun1_has_sensors == 0 assert m.flags.lun2_has_sensors == 0 assert m.flags.lun3_has_sensors == 0 assert m.flags.dynamic_population == 0 assert m.sensor_population_change == 0xddccbbaa def test_getdevicesdr_encode_req(): m = pyipmi.msgs.sensor.GetDeviceSdrReq() m.reservation_id = 0x0123 m.record_id = 0x4567 m.offset = 0x89 m.bytes_to_read = 0xab data = encode_message(m) assert data == b'\x23\x01\x67\x45\x89\xab' def test_getdevicesdr_decode_rsp(): m = pyipmi.msgs.sensor.GetDeviceSdrRsp() decode_message(m, b'\x00\x01\x23\xaa\xbb') assert m.completion_code == 0x00 assert m.next_record_id == 0x2301 assert m.record_data == array('B', [0xaa, 0xbb]) def test_setsensorhysteresis_encode_req(): m = pyipmi.msgs.sensor.SetSensorHysteresisReq() m.sensor_number = 0xab m.positive_going_hysteresis = 0xaa m.negative_going_hysteresis = 0xbb data = encode_message(m) assert data == b'\xab\xff\xaa\xbb' def test_getsensorhysteresis_encode_req(): m = pyipmi.msgs.sensor.GetSensorHysteresisReq() m.sensor_number = 0xab data = encode_message(m) assert data == b'\xab\xff' def test_getsensorhysteresis_decode_rsp(): m = pyipmi.msgs.sensor.GetSensorHysteresisRsp() decode_message(m, b'\x00\xaa\xbb') assert m.completion_code == 0x00 assert m.positive_going_hysteresis == 0xaa assert m.negative_going_hysteresis == 0xbb def test_setsensorthresholds_encode_req_set_unr(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.unr = 1 m.threshold.unr = 0xaa data = encode_message(m) assert data == b'\x55\x20\x00\x00\x00\x00\x00\xaa' def test_setsensorthresholds_encode_req_set_ucr(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.ucr = 1 m.threshold.ucr = 0xaa data = encode_message(m) assert data == b'\x55\x10\x00\x00\x00\x00\xaa\x00' def test_setsensorthresholds_encode_req_set_unc(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.unc = 1 m.threshold.unc = 0xaa data = encode_message(m) assert data == b'\x55\x08\x00\x00\x00\xaa\x00\x00' def test_setsensorthresholds_encode_req_set_lnr(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.lnr = 1 m.threshold.lnr = 0xaa data = encode_message(m) assert data == b'\x55\x04\x00\x00\xaa\x00\x00\x00' def test_setsensorthresholds_encode_req_set_lcr(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.lcr = 1 m.threshold.lcr = 0xaa data = encode_message(m) assert data == b'\x55\x02\x00\xaa\x00\x00\x00\x00' def test_setsensorthresholds_encode_req_set_lnc(): m = pyipmi.msgs.sensor.SetSensorThresholdsReq() m.sensor_number = 0x55 m.set_mask.lnc = 1 m.threshold.lnc = 0xaa data = encode_message(m) assert data == b'\x55\x01\xaa\x00\x00\x00\x00\x00' def test_setsensoreventenable_encode_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 0 m.enable.event_message = 0 m.enable.sensor_scanning = 0 data = encode_message(m) assert data == b'\xab\x00' def test_setsensoreventenable_encode_cfg_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 2 m.enable.event_message = 0 m.enable.sensor_scanning = 0 data = encode_message(m) assert data == b'\xab\x20' def test_setsensoreventenable_encode_scanning_enabled_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 0 m.enable.event_message = 0 m.enable.sensor_scanning = 1 data = encode_message(m) assert data == b'\xab\x40' def test_setsensoreventenable_encode_event_enabled_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 0 m.enable.event_message = 1 m.enable.sensor_scanning = 0 data = encode_message(m) assert data == b'\xab\x80' def test_setsensoreventenable_encode_byte3_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 0 m.enable.event_message = 0 m.enable.sensor_scanning = 0 m.byte3 = 0xaa data = encode_message(m) assert data == b'\xab\x00\xaa' def test_setsensoreventenable_encode_byte34_req(): m = pyipmi.msgs.sensor.SetSensorEventEnableReq() m.sensor_number = 0xab m.enable.config = 0 m.enable.event_message = 0 m.enable.sensor_scanning = 0 m.byte3 = 0xaa m.byte4 = 0xbb data = encode_message(m) assert data == b'\xab\x00\xaa\xbb' def test_getsensoreventenable_encode_req(): m = pyipmi.msgs.sensor.GetSensorEventEnableReq() m.sensor_number = 0xab data = encode_message(m) assert data == b'\xab' def test_getsensoreventenable_decode_event_enabled_rsp(): m = pyipmi.msgs.sensor.GetSensorEventEnableRsp() decode_message(m, b'\x00\x80') assert m.completion_code == 0x00 assert m.enabled.event_message == 1 assert m.enabled.sensor_scanning == 0 def test_getsensoreventenable_decode_scanning_enabled_rsp(): m = pyipmi.msgs.sensor.GetSensorEventEnableRsp() decode_message(m, b'\x00\x40') assert m.completion_code == 0x00 assert m.enabled.event_message == 0 assert m.enabled.sensor_scanning == 1 def test_getsensoreventenable_decode_byte3_rsp(): m = pyipmi.msgs.sensor.GetSensorEventEnableRsp() decode_message(m, b'\x00\xc0\xaa') assert m.completion_code == 0x00 assert m.enabled.event_message == 1 assert m.byte3 == 0xaa def test_getsensoreventenable_decode_byte34_rsp(): m = pyipmi.msgs.sensor.GetSensorEventEnableRsp() decode_message(m, b'\x00\xc0\xaa\xbb') assert m.completion_code == 0x00 assert m.enabled.event_message == 1 assert m.byte3 == 0xaa assert m.byte4 == 0xbb def test_getsensoreventenable_decode_byte3456_rsp(): m = pyipmi.msgs.sensor.GetSensorEventEnableRsp() decode_message(m, b'\x00\xc0\xaa\xbb\xcc\xdd') assert m.completion_code == 0x00 assert m.enabled.event_message == 1 assert m.byte3 == 0xaa assert m.byte4 == 0xbb assert m.byte5 == 0xcc assert m.byte6 == 0xdd def test_rearmsensorevents_encode_req(): m = pyipmi.msgs.sensor.RearmSensorEventsReq() m.sensor_number = 0xab data = encode_message(m) assert data == b'\xab\x00\x00\x00\x00\x00' def test_rearmsensorevents_decode_rsp(): m = pyipmi.msgs.sensor.RearmSensorEventsRsp() decode_message(m, b'\x00') assert m.completion_code == 0x00 def test_platform_event_encode_req(): m = pyipmi.msgs.sensor.PlatformEventReq() m.sensor_type = 0xf2 m.sensor_number = 0xab m.event_type.type = 0x6f m.event_type.dir = 0x0 m.event_data = [0x1, 0xff, 0xff] data = encode_message(m) assert data == b'\x04\xf2\xab\x6f\x01\xff\xff' def test_platform_event_decode_rsp(): m = pyipmi.msgs.sensor.PlatformEventRsp() decode_message(m, b'\x00') assert m.completion_code == 0x00 python-ipmi-0.5.4/tests/test_bmc.py000066400000000000000000000026011436677633700173360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from pyipmi.bmc import DeviceId, Watchdog import pyipmi.msgs.bmc from pyipmi.msgs import decode_message def test_watchdog_object(): msg = pyipmi.msgs.bmc.GetWatchdogTimerRsp() decode_message(msg, b'\x00\x41\x42\x33\x44\x55\x66\x77\x88') wdt = Watchdog(msg) assert wdt.timer_use == 1 assert wdt.is_running == 1 assert wdt.dont_log == 0 assert wdt.timeout_action == 2 assert wdt.pre_timeout_interrupt == 4 assert wdt.pre_timeout_interval == 0x33 assert wdt.timer_use_expiration_flags == 0x44 assert wdt.initial_countdown == 0x6655 assert wdt.present_countdown == 0x8877 def test_deviceid_object(): rsp = pyipmi.msgs.bmc.GetDeviceIdRsp() decode_message(rsp, b'\x00\x12\x84\x05\x67\x51\x55\x12\x34\x56\x44\x55') dev = DeviceId(rsp) assert dev.device_id == 18 assert dev.revision == 4 assert dev.provides_sdrs assert str(dev.fw_revision) == '5.67' assert str(dev.ipmi_version) == '1.5' assert dev.manufacturer_id == 5649426 assert dev.product_id == 21828 assert dev.aux is None def test_deviceid_object_with_aux(): msg = pyipmi.msgs.bmc.GetDeviceIdRsp() decode_message(msg, b'\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x01\x02\x03\x04') device_id = DeviceId(msg) assert device_id.aux == [1, 2, 3, 4] python-ipmi-0.5.4/tests/test_chassis.py000066400000000000000000000043721436677633700202410ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from array import array from pyipmi.chassis import (ChassisStatus, data_to_boot_device, data_to_boot_mode, data_to_boot_persistency, boot_options_to_data, BootDevice) import pyipmi.msgs.chassis from pyipmi.msgs import decode_message def test_chassisstatus_object(): msg = pyipmi.msgs.chassis.GetChassisStatusRsp() decode_message(msg, b'\x00\xff\xff\xff') status = ChassisStatus(msg) assert status.power_on assert status.overload assert status.interlock assert status.fault assert status.control_fault assert status.restore_policy == 3 assert 'ac_failed' in status.last_event assert 'overload' in status.last_event assert 'interlock' in status.last_event assert 'fault' in status.last_event assert 'power_on_via_ipmi' in status.last_event assert 'intrusion', status.chassis_state assert 'front_panel_lockout', status.chassis_state assert 'drive_fault', status.chassis_state assert 'cooling_fault', status.chassis_state def test_datatobootmode(): assert data_to_boot_mode(array('B', [0, 0, 0, 0, 0])) == "legacy" assert data_to_boot_mode(array('B', [0b10100000, 0, 0, 0, 0])) == "efi" def test_datatobootpersistency(): assert data_to_boot_persistency(array('B', [0b11000000, 0, 0, 0, 0])) assert not data_to_boot_persistency(array('B', [0b10000000, 0, 0, 0, 0])) def test_datatobootdevice(): assert data_to_boot_device(array('B', [0b11000000, 0b00001000, 0, 0, 0])) == BootDevice.DEFAULT_HDD assert data_to_boot_device(array('B', [0b11000000, 0b00000100, 0, 0, 0])) == BootDevice.PXE def test_bootoptionstodata(): assert boot_options_to_data("bios setup", "efi", True).array == array('B', [0b11100000, 0b00011000, 0, 0, 0]) def test_bootoptionstodata_raise_typeerror(): with pytest.raises(TypeError): boot_options_to_data("pxe", "efi", 1) def test_bootoptionstodata_raise_valueerror_bootmode(): with pytest.raises(ValueError): boot_options_to_data("pxe", "wrong boot mode", True) def test_bootoptionstodata_raise_valueerror_bootdevice(): with pytest.raises(ValueError): boot_options_to_data("wrong boot device", "efi", True) python-ipmi-0.5.4/tests/test_device_messaging.py000066400000000000000000000013321436677633700220710ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from pyipmi.messaging import UserAccess, UserPrivilegeLevel import pyipmi.msgs.device_messaging from pyipmi.msgs import decode_message def test_useraccess_object(): msg = pyipmi.msgs.device_messaging.GetUserAccessRsp() decode_message(msg, b'\x00\x0a\x42\x01\x13') user_access = UserAccess(msg) assert user_access.user_count == 10 assert user_access.enabled_user_count == 2 assert user_access.enabled_status == 1 assert user_access.fixed_name_user_count == 1 assert user_access.privilege_level == UserPrivilegeLevel.OPERATOR assert user_access.ipmi_messaging == 1 assert user_access.link_auth == 0 assert user_access.callback_only == 0 python-ipmi-0.5.4/tests/test_errors.py000066400000000000000000000020551436677633700201140ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from pyipmi.errors import (DecodingError, EncodingError, CompletionCodeError, NotSupportedError, DescriptionError, RetryError, DataNotFound, HpmError) def test_DecodingError(): with pytest.raises(DecodingError): raise DecodingError() def test_EncodingError(): with pytest.raises(EncodingError): raise EncodingError() def test_CompletionCodeError(): with pytest.raises(CompletionCodeError): raise CompletionCodeError(cc=0x09) def test_NotSupportedError(): with pytest.raises(NotSupportedError): raise NotSupportedError() def test_DescriptionError(): with pytest.raises(DescriptionError): raise DescriptionError() def test_RetryError(): with pytest.raises(RetryError): raise RetryError() def test_DataNotFound(): with pytest.raises(DataNotFound): raise DataNotFound() def test_HpmError_no_msg(): with pytest.raises(HpmError): raise HpmError() python-ipmi-0.5.4/tests/test_fields.py000066400000000000000000000012101436677633700200360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from pyipmi.fields import VersionField from pyipmi.errors import DecodingError def test_versionfield_object(): version = VersionField([1, 0x99]) assert version.major == 1 assert version.minor == 99 version = VersionField('\x00\x99') assert version.major == 0 assert version.minor == 99 def test_versionfield_invalid(): version = VersionField('\x00\xff') assert version.major == 0 assert version.minor == 255 def test_versionfield_decoding_error(): with pytest.raises(DecodingError): version = VersionField('\x00\x9a') # noqa:F841 python-ipmi-0.5.4/tests/test_fru.py000066400000000000000000000043461436677633700174010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os from pyipmi.fru import (FruData, FruPicmgPowerModuleCapabilityRecord, InventoryCommonHeader, get_fru_inventory_from_file) def test_frudata_object(): fru_field = FruData((0, 1, 2, 3)) assert fru_field.data[0] == 0 assert fru_field.data[1] == 1 assert fru_field.data[2] == 2 assert fru_field.data[3] == 3 fru_field = FruData('\x00\x01\x02\x03') assert fru_field.data[0] == 0 assert fru_field.data[1] == 1 assert fru_field.data[2] == 2 assert fru_field.data[3] == 3 def test_commonheader_object(): InventoryCommonHeader((0, 1, 2, 3, 4, 5, 6, 235)) def test_fru_inventory_from_file(): path = os.path.dirname(os.path.abspath(__file__)) fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin') fru = get_fru_inventory_from_file(fru_file) assert fru.chassis_info_area is None def test_board_area(): path = os.path.dirname(os.path.abspath(__file__)) fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin') fru = get_fru_inventory_from_file(fru_file) board_area = fru.board_info_area assert board_area.manufacturer.string == 'Kontron' assert board_area.product_name.string == 'AM4010' assert board_area.serial_number.string == '0023721003' assert board_area.part_number.string == '35943' def test_product_area(): path = os.path.dirname(os.path.abspath(__file__)) fru_file = os.path.join(path, 'fru_bin/kontron_am4010.bin') fru = get_fru_inventory_from_file(fru_file) product_area = fru.product_info_area assert product_area.manufacturer.string == 'Kontron' assert product_area.name.string == 'AM4010' assert product_area.serial_number.string == '0000000000000000000000000' assert product_area.part_number.string == '0012' def test_multirecord_with_power_module_capability_record(): path = os.path.dirname(os.path.abspath(__file__)) fru_file = os.path.join(path, 'fru_bin/vadatech_utc017.bin') fru = get_fru_inventory_from_file(fru_file) assert len(fru.multirecord_area.records) == 1 record = fru.multirecord_area.records[0] assert isinstance(record, FruPicmgPowerModuleCapabilityRecord) assert record.maximum_current_output == 42.0 python-ipmi-0.5.4/tests/test_helper.py000066400000000000000000000017261436677633700200630ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from mock import MagicMock, call from pyipmi.helper import clear_repository_helper from pyipmi.msgs.constants import (REPOSITORY_ERASURE_COMPLETED, REPOSITORY_ERASURE_IN_PROGRESS, REPOSITORY_INITIATE_ERASE, REPOSITORY_GET_ERASE_STATUS) def test_clear_repository_helper(): reserve_fn = MagicMock() reserve_fn.return_value = (0x1234) clear_fn = MagicMock() clear_fn.side_effect = [ REPOSITORY_ERASURE_COMPLETED, REPOSITORY_ERASURE_IN_PROGRESS, REPOSITORY_ERASURE_COMPLETED, ] clear_repository_helper(reserve_fn, clear_fn) clear_calls = [ call(REPOSITORY_INITIATE_ERASE, 0x1234), call(REPOSITORY_GET_ERASE_STATUS, 0x1234), call(REPOSITORY_GET_ERASE_STATUS, 0x1234), ] clear_fn.assert_has_calls(clear_calls) assert clear_fn.call_count == 3 python-ipmi-0.5.4/tests/test_hpm.py000066400000000000000000000076571436677633700174010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os from pyipmi.hpm import (ComponentProperty, ComponentPropertyDescriptionString, ComponentPropertyGeneral, ComponentPropertyCurrentVersion, ComponentPropertyDeferredVersion, ComponentPropertyRollbackVersion, UpgradeActionRecord, UpgradeActionRecordBackup, UpgradeActionRecordPrepare, UpgradeActionRecordUploadForUpgrade, UpgradeActionRecordUploadForCompare, UpgradeImage, PROPERTY_GENERAL_PROPERTIES, PROPERTY_CURRENT_VERSION, PROPERTY_DESCRIPTION_STRING, PROPERTY_ROLLBACK_VERSION, PROPERTY_DEFERRED_VERSION) class TestComponentProperty(object): def test_general(self): prop = ComponentProperty().from_data(PROPERTY_GENERAL_PROPERTIES, b'\xaa') assert type(prop) == ComponentPropertyGeneral prop = ComponentProperty().from_data(PROPERTY_GENERAL_PROPERTIES, (0xaa,)) assert type(prop) == ComponentPropertyGeneral def test_currentversion(self): prop = ComponentProperty().from_data(PROPERTY_CURRENT_VERSION, b'\x01\x99') assert type(prop) == ComponentPropertyCurrentVersion prop = ComponentProperty().from_data( PROPERTY_CURRENT_VERSION, (0x01, 0x99)) assert type(prop) == ComponentPropertyCurrentVersion def test_descriptionstring(self): prop = ComponentProperty().from_data(PROPERTY_DESCRIPTION_STRING, b'\x30\x31\x32') assert type(prop) == ComponentPropertyDescriptionString assert prop.description == '012' prop = ComponentProperty().from_data( PROPERTY_DESCRIPTION_STRING, (0x33, 0x34, 0x35)) assert type(prop) == ComponentPropertyDescriptionString assert prop.description == '345' def test_descriptionstring_with_trailinge_zeros(self): prop = ComponentProperty().from_data(PROPERTY_DESCRIPTION_STRING, b'\x36\x37\x38\x00\x00') assert type(prop) == ComponentPropertyDescriptionString assert prop.description == '678' def test_rollbackversion(self): prop = ComponentProperty().from_data( PROPERTY_ROLLBACK_VERSION, (0x2, 0x88)) assert type(prop) == ComponentPropertyRollbackVersion def test_deferredversion(self): prop = ComponentProperty().from_data( PROPERTY_DEFERRED_VERSION, (0x3, 0x77)) assert type(prop) == ComponentPropertyDeferredVersion def test_upgradeactionrecord_create_from_data(): record = UpgradeActionRecord.create_from_data(b'\x00\x08\x02') assert record.action == 0 assert type(record) == UpgradeActionRecordBackup record = UpgradeActionRecord.create_from_data(b'\x01\x08\x02') assert record.action == 1 assert type(record) == UpgradeActionRecordPrepare record = \ UpgradeActionRecord.create_from_data( b'\x02\x08\x02' b'\x01\x99\xaa\xbb\xcc\xdd' b'\x30\x31\x32\x33\x34\x35\x36\x37' b'\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30' b'\x04\x00\x00\x00\x11\x22\x33\x44') assert record.firmware_version.version_to_string() == '1.99' assert record.firmware_description_string == '012345678901234567890' assert record.firmware_length == 4 record = UpgradeActionRecord.create_from_data(b'\x03\x08\x02') assert record.action == 3 assert type(record) == UpgradeActionRecordUploadForCompare def test_upgrade_image(): path = os.path.dirname(os.path.abspath(__file__)) hpm_file = os.path.join(path, 'hpm_bin/firmware.hpm') image = UpgradeImage(hpm_file) assert isinstance(image.actions[0], UpgradeActionRecordPrepare) assert isinstance(image.actions[1], UpgradeActionRecordUploadForUpgrade) python-ipmi-0.5.4/tests/test_ipmi.py000066400000000000000000000111071436677633700175340ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from mock import MagicMock from pyipmi import interfaces, create_connection, Target, Routing from pyipmi.errors import CompletionCodeError, RetryError from pyipmi.msgs.bmc import GetDeviceIdReq, GetDeviceIdRsp from pyipmi.msgs.sensor import GetSensorReadingReq, GetSensorReadingRsp from pyipmi.msgs.constants import CC_NODE_BUSY def test_target(): target = Target() assert target.ipmb_address is None assert target.routing is None target = Target(0xa0) assert target.ipmb_address == 0xa0 assert target.routing is None target = Target(ipmb_address=0xb0) assert target.ipmb_address == 0xb0 assert target.routing is None def test_target_routing(): target = Target(routing=[(0x82, 0x20, 0)]) assert len(target.routing) == 1 assert target.routing[0].rq_sa == 0x82 assert target.routing[0].rs_sa == 0x20 assert target.routing[0].channel == 0 target = Target(routing=[(0x82, 0x20, 0), (0x20, 0x82, 7)]) assert len(target.routing) == 2 assert target.routing[0].rq_sa == 0x82 assert target.routing[0].rs_sa == 0x20 assert target.routing[0].channel == 0 assert target.routing[1].rq_sa == 0x20 assert target.routing[1].rs_sa == 0x82 assert target.routing[1].channel == 7 def test_routing(): routing = Routing(0x82, 0x20, 7) assert routing.rq_sa == 0x82 assert routing.rs_sa == 0x20 assert routing.channel == 7 def test_target_set_routing(): target = Target() target.set_routing([(0x11, 0x12, 0x13)]) assert len(target.routing) == 1 assert target.routing[0].rq_sa == 0x11 assert target.routing[0].rs_sa == 0x12 assert target.routing[0].channel == 0x13 target.set_routing([(0x11, 0x12, 0x13), (0x21, 0x22, 0x23)]) assert len(target.routing) == 2 assert target.routing[0].rq_sa == 0x11 assert target.routing[0].rs_sa == 0x12 assert target.routing[0].channel == 0x13 assert target.routing[1].rq_sa == 0x21 assert target.routing[1].rs_sa == 0x22 assert target.routing[1].channel == 0x23 def test_target_set_routing_from_string(): target = Target() target.set_routing('[(0x11, 0x12, 0x13)]') assert len(target.routing) == 1 assert target.routing[0].rq_sa == 0x11 assert target.routing[0].rs_sa == 0x12 assert target.routing[0].channel == 0x13 target.set_routing('[(0x11, 0x12, 0x13), (0x21, 0x22, 0x23)]') assert len(target.routing) == 2 assert target.routing[0].rq_sa == 0x11 assert target.routing[0].rs_sa == 0x12 assert target.routing[0].channel == 0x13 assert target.routing[1].rq_sa == 0x21 assert target.routing[1].rs_sa == 0x22 assert target.routing[1].channel == 0x23 def test_ipmi_send_message_retry(): req = GetDeviceIdReq() rsp = GetDeviceIdRsp() interface = interfaces.create_interface('mock') cc = CompletionCodeError(CC_NODE_BUSY) mock = MagicMock(side_effect=(cc, 0)) mock.return_value = rsp interface.send_and_receive = mock ipmi = create_connection(interface) ipmi.target = None ipmi.send_message(req) assert mock.call_count == 2 def test_ipmi_send_message_retry_error(): req = GetDeviceIdReq() rsp = GetDeviceIdRsp() with pytest.raises(RetryError): interface = interfaces.create_interface('mock') cc = CompletionCodeError(CC_NODE_BUSY) mock = MagicMock(side_effect=(cc, cc, cc)) mock.return_value = rsp interface.send_and_receive = mock ipmi = create_connection(interface) ipmi.target = None ipmi.send_message(req) def test_ipmi_send_message_with_name(): rsp = GetDeviceIdRsp() rsp.completion_code = 0 mock_send_message = MagicMock() mock_send_message.return_value = rsp interface = interfaces.create_interface('mock') ipmi = create_connection(interface) ipmi.send_message = mock_send_message ipmi.send_message_with_name('GetDeviceId') args, kwargs = mock_send_message.call_args req = args[0] assert isinstance(req, GetDeviceIdReq) def test_ipmi_send_message_with_name_and_kwargs(): rsp = GetSensorReadingRsp() rsp.completion_code = 0 mock_send_message = MagicMock() mock_send_message.return_value = rsp interface = interfaces.create_interface('mock') ipmi = create_connection(interface) ipmi.send_message = mock_send_message ipmi.send_message_with_name('GetSensorReading', sensor_number=5, lun=2) args, kwargs = mock_send_message.call_args req = args[0] assert isinstance(req, GetSensorReadingReq) assert req.sensor_number == 5 assert req.lun == 2 python-ipmi-0.5.4/tests/test_lan.py000066400000000000000000000034661436677633700173610ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from array import array from pyipmi.lan import (data_to_ip_address, data_to_ip_source, data_to_mac_address, data_to_vlan, ip_address_to_data, ip_source_to_data, vlan_to_data) def test_datatoipaddress(): assert data_to_ip_address(array('B', [192, 168, 1, 1])) == "192.168.1.1" def test_ipaddresstodata(): assert ip_address_to_data("192.168.1.1").array == array('B', [192, 168, 1, 1]) def test_datatoipsource(): assert data_to_ip_source(array('B', [0])) == "unknown" assert data_to_ip_source(array('B', [1])) == "static" assert data_to_ip_source(array('B', [2])) == "dhcp" assert data_to_ip_source(array('B', [3])) == "bios" assert data_to_ip_source(array('B', [4])) == "other" def test_ipsourcetodata(): assert ip_source_to_data("static").array == array('B', [1]) assert ip_source_to_data("dhcp").array == array('B', [2]) def test_ipsourcetodata_raise_valueerror(): with pytest.raises(ValueError): ip_source_to_data("does not exist") def test_datatomacaddress(): assert data_to_mac_address(array('B', [0xab, 0xcd, 0xef, 0x12, 0x34, 0x56])) == "ab:cd:ef:12:34:56" def test_datatovlan(): assert data_to_vlan(array('B', [138, 129])) == 394 assert data_to_vlan(array('B', [0, 0])) == 0 assert data_to_vlan(array('B', [19, 128])) == 19 def test_vlantodata(): assert vlan_to_data(394).array == array('B', [138, 129]) assert vlan_to_data(0).array == array('B', [0, 0]) assert vlan_to_data(19).array == array('B', [19, 128]) def test_vlantodata_raise_typeerror(): with pytest.raises(TypeError): vlan_to_data("wrong type of argument") def test_vlantodata_raise_valueerror(): with pytest.raises(ValueError): vlan_to_data(4096) python-ipmi-0.5.4/tests/test_picmg.py000066400000000000000000000034501436677633700176770ustar00rootroot00000000000000#!/usr/bin/env python from pyipmi.picmg import LedState from pyipmi.msgs.picmg import SetFruLedStateReq def test_to_request(): req = SetFruLedStateReq() led = LedState(fru_id=1, led_id=2, color=LedState.COLOR_GREEN, function=LedState.FUNCTION_ON) led.to_request(req) assert req.fru_id == 1 assert req.led_id == 2 assert req.color == led.COLOR_GREEN assert req.led_function == 0xff assert req.on_duration == 0 def test_to_request_function_on(): req = SetFruLedStateReq() led = LedState(fru_id=1, led_id=2, color=LedState.COLOR_RED) led.override_function = led.FUNCTION_ON led.to_request(req) assert req.color == LedState.COLOR_RED assert req.led_function == 0xff assert req.on_duration == 0 def test_to_request_function_off(): req = SetFruLedStateReq() led = LedState(fru_id=1, led_id=2, color=LedState.COLOR_RED) led.override_function = led.FUNCTION_OFF led.to_request(req) assert req.color == LedState.COLOR_RED assert req.led_function == 0 assert req.on_duration == 0 def test_to_request_function_blinking(): req = SetFruLedStateReq() led = LedState(fru_id=1, led_id=2, color=LedState.COLOR_RED) led.override_function = led.FUNCTION_BLINKING led.override_off_duration = 3 led.override_on_duration = 4 led.to_request(req) assert req.color == LedState.COLOR_RED assert req.led_function == 3 assert req.on_duration == 4 def test_to_request_function_lamp_test(): req = SetFruLedStateReq() led = LedState(fru_id=1, led_id=2, color=LedState.COLOR_RED) led.override_function = led.FUNCTION_LAMP_TEST led.lamp_test_duration = 3 led.to_request(req) assert req.color == LedState.COLOR_RED assert req.led_function == 0xfb assert req.on_duration == 3 python-ipmi-0.5.4/tests/test_sdr.py000066400000000000000000000235121436677633700173710ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from pyipmi.errors import DecodingError from pyipmi.sdr import (SdrCommon, SdrFullSensorRecord, SdrCompactSensorRecord, SdrEventOnlySensorRecord, SdrFruDeviceLocator, SdrManagementControllerDeviceLocator, SdrManagementControllerConfirmationRecord, SdrUnknownSensorRecord) class TestSdrFullSensorRecord(): def test_convert_complement(self): assert SdrFullSensorRecord()._convert_complement(0x8, 4) == -8 assert SdrFullSensorRecord()._convert_complement(0x80, 8) == -128 assert SdrFullSensorRecord()._convert_complement(0x8000, 16) == -32768 def test_decode_capabilities(self): record = SdrFullSensorRecord() record._decode_capabilities(0) assert 'ignore_sensor' not in record.capabilities assert 'auto_rearm' not in record.capabilities assert 'hysteresis_not_supported' in record.capabilities assert 'threshold_not_supported' in record.capabilities record._decode_capabilities(0x80) assert 'ignore_sensor' in record.capabilities assert 'auto_rearm' not in record.capabilities assert 'hysteresis_not_supported' in record.capabilities assert 'threshold_not_supported' in record.capabilities record._decode_capabilities(0x40) assert 'ignore_sensor' not in record.capabilities assert 'auto_rearm' in record.capabilities assert 'hysteresis_not_supported' in record.capabilities assert 'threshold_not_supported' in record.capabilities record._decode_capabilities(0x30) assert 'ignore_sensor' not in record.capabilities assert 'auto_rearm' not in record.capabilities assert 'hysteresis_fixed' in record.capabilities assert 'threshold_not_supported' in record.capabilities record._decode_capabilities(0x0c) assert 'ignore_sensor' not in record.capabilities assert 'auto_rearm' not in record.capabilities assert 'hysteresis_not_supported' in record.capabilities assert 'threshold_fixed' in record.capabilities def test_invalid_length(self): with pytest.raises(DecodingError): data = (0, 0, 0, 0, 0) SdrFullSensorRecord(data) def test_linearization_key_error(self): with pytest.raises(DecodingError): sdr = SdrFullSensorRecord(None) sdr.linearization = 12 sdr.lin(1) def test_linearization(self): sdr = SdrFullSensorRecord(None) # linear sdr.linearization = 0 assert sdr.lin(1) == 1 assert sdr.lin(10) == 10 # ln sdr.linearization = 1 assert sdr.lin(1) == 0 # log sdr.linearization = 2 assert sdr.lin(10) == 1 assert sdr.lin(100) == 2 # log sdr.linearization = 3 assert sdr.lin(8) == 3 assert sdr.lin(16) == 4 # e sdr.linearization = 4 assert sdr.lin(1) == 2.718281828459045 # exp10 sdr.linearization = 5 assert sdr.lin(1) == 10 assert sdr.lin(2) == 100 # exp2 sdr.linearization = 6 assert sdr.lin(3) == 8 assert sdr.lin(4) == 16 # 1/x sdr.linearization = 7 assert sdr.lin(2) == 0.5 assert sdr.lin(4) == 0.25 # sqr sdr.linearization = 8 assert sdr.lin(2) == 4 # cube sdr.linearization = 9 assert sdr.lin(2) == 8 assert sdr.lin(3) == 27 # sqrt sdr.linearization = 10 assert sdr.lin(16) == 4 # cubert sdr.linearization = 11 assert sdr.lin(8) == 2 assert sdr.lin(27) == 3 def test_convert_sensor_raw_to_value(self): sdr = SdrFullSensorRecord() assert sdr.convert_sensor_raw_to_value(None) is None sdr.analog_data_format = sdr.DATA_FMT_UNSIGNED sdr.m = 1 sdr.b = 0 sdr.k1 = 0 sdr.k2 = 0 sdr.linearization = 0 assert sdr.convert_sensor_raw_to_value(1) == 1 assert sdr.convert_sensor_raw_to_value(255) == 255 sdr.analog_data_format = sdr.DATA_FMT_UNSIGNED sdr.m = 10 sdr.b = 0 sdr.k1 = 0 sdr.k2 = 0 sdr.linearization = 0 assert sdr.convert_sensor_raw_to_value(1) == 10 assert sdr.convert_sensor_raw_to_value(255) == 2550 sdr.analog_data_format = sdr.DATA_FMT_1S_COMPLEMENT sdr.m = 1 sdr.b = 0 sdr.k1 = 0 sdr.k2 = 0 sdr.linearization = 0 assert sdr.convert_sensor_raw_to_value(1) == 1 assert sdr.convert_sensor_raw_to_value(128) == -127 assert sdr.convert_sensor_raw_to_value(255) == 0 sdr.analog_data_format = sdr.DATA_FMT_2S_COMPLEMENT sdr.m = 1 sdr.b = 0 sdr.k1 = 0 sdr.k2 = 0 sdr.linearization = 0 assert sdr.convert_sensor_raw_to_value(1) == 1 assert sdr.convert_sensor_raw_to_value(128) == -128 assert sdr.convert_sensor_raw_to_value(255) == -1 def test_decocde(self): data = [0x17, 0x00, 0x51, 0x01, 0x35, 0x17, 0x00, 0x51, 0x01, 0x35, 0x17, 0x00, 0x51, 0x01, 0x35, 0x32, 0x85, 0x32, 0x1b, 0x1b, 0x00, 0x04, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x01, 0x00, 0xd0, 0x07, 0xcc, 0xf4, 0xa6, 0xff, 0x00, 0x00, 0xfe, 0xf5, 0x00, 0x8e, 0xa5, 0x04, 0x04, 0x00, 0x00, 0x00, 0xca, 0x41, 0x32, 0x3a, 0x56, 0x63, 0x63, 0x20, 0x31, 0x32, 0x56] sdr = SdrCommon.from_data(data) assert isinstance(sdr, SdrFullSensorRecord) assert str(sdr) == '["A2:Vcc 12V"] [1:53] [17 00 51 01 35 17 00 ' \ '51 01 35 17 00 51 01 35 32 85 32 1b 1b 00 04 00 00 ' \ '3b 01 00 01 00 d0 07 cc f4 a6 ff 00 00 fe f5 00 8e ' \ 'a5 04 04 00 00 00 ca 41 32 3a 56 63 63 20 31 32 56]' assert sdr.device_id_string == 'A2:Vcc 12V' class TestSdrCommon(): def test_invalid_data_length(self): with pytest.raises(DecodingError): data = (0x00, 0x01, 0x02, 0x03) SdrCommon(data) def test_object(self): data = (0x00, 0x01, 0x02, 0x03, 0x04) sdr = SdrCommon(data) assert sdr.id == 0x0100 assert sdr.version == 0x02 assert sdr.type == 0x03 assert sdr.length == 0x04 class TestSdrCompactSensorRecord(): def test_invalid_length(self): with pytest.raises(DecodingError): data = (0, 0, 0, 0, 0) SdrCompactSensorRecord(data) def test_decode(self): data = [0xd3, 0x00, 0x51, 0x02, 0x28, 0x82, 0x00, 0xd3, 0xc1, 0x64, 0x03, 0x40, 0x21, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x41, 0x34, 0x3a, 0x50, 0x72, 0x65, 0x73, 0x20, 0x53, 0x46, 0x50, 0x2d, 0x31] sdr = SdrCommon.from_data(data) assert isinstance(sdr, SdrCompactSensorRecord) assert str(sdr) == '["A4:Pres SFP-1"] [d3 00 51 02 28 82 00 d3 c1 64 ' \ '03 40 21 6f 00 00 00 00 03 00 c0 00 00 01 00 00 00 ' \ '00 00 00 00 cd 41 34 3a 50 72 65 73 20 53 46 50 2d 31]' def test_sdreventonlysensorrecord(): with pytest.raises(DecodingError): data = (0, 0, 0, 0, 0) SdrEventOnlySensorRecord(data) class TestSdrFruDeviceLocatorRecord(): def test_invalid_length(self): with pytest.raises(DecodingError): data = (0, 0, 0, 0, 0) SdrFruDeviceLocator(data) def test_decode(self): data = [0x02, 0x00, 0x51, 0x11, 0x17, 0x82, 0x03, 0x80, 0x00, 0x00, 0x10, 0x02, 0xc2, 0x61, 0x00, 0xcc, 0x4b, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6e, 0x20, 0x4d, 0x43, 0x4d, 0x43] sdr = SdrCommon.from_data(data) assert isinstance(sdr, SdrFruDeviceLocator) assert str(sdr) == '["Kontron MCMC"] [02 00 51 11 17 82 03 80 00 00 ' \ '10 02 c2 61 00 cc 4b 6f 6e 74 72 6f 6e 20 4d 43 4d 43]' class TestSdrManagementControllerDeviceRecord(): def test_invalid_length(self): with pytest.raises(DecodingError): data = (0, 0, 0, 0, 0) SdrManagementControllerDeviceLocator(data) def test_decode(self): data = [0x00, 0x01, 0x51, 0x12, 0x19, 0x00, 0x01, 0x51, 0x12, 0x1b, 0x00, 0x01, 0x51, 0x12, 0x1b, 0xc9, 0x41, 0x32, 0x3a, 0x41, 0x4d, 0x34, 0x32, 0x32, 0x30] sdr = SdrCommon.from_data(data) assert isinstance(sdr, SdrManagementControllerDeviceLocator) assert str(sdr) == '["A2:AM4220"] [00 01 51 12 19 00 01 51 12 1b ' \ '00 01 51 12 1b c9 41 32 3a 41 4d 34 32 32 30]' assert sdr.device_id_string == 'A2:AM4220' class TestSdrManagementControllerConfirmationRecord(): def test_decode(self): data = [0x45, 0x00, 0x51, 0x13, 0x1b, 0x20, 0x00, 0x01, 0x02, 0x01, 0x51, 0x4a, 0xc1, 0x62, 0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] sdr = SdrCommon.from_data(data) assert isinstance(sdr, SdrManagementControllerConfirmationRecord) assert str(sdr) == '[45 00 51 13 1b 20 00 01 02 01 51 4a c1 62 06 80 00 ' \ '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00]' assert sdr.ipmi_version == 0x51 assert sdr.manufacturer_id == 0x2c14a assert sdr.product_id == 0x8006 def test_unknown_record_type(): data = [0x01, 0x0, 0x51, 0x0a, 0x0] sdr = SdrCommon.from_data(data, 0xffff) assert isinstance(sdr, SdrUnknownSensorRecord) python-ipmi-0.5.4/tests/test_sel.py000066400000000000000000000136351436677633700173710ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from mock import MagicMock from pyipmi import interfaces, create_connection from pyipmi.msgs.registry import create_response_by_name from pyipmi.sel import SelEntry, SelInfo class TestSel(object): def test_get_sel_entries(self): rsps = list() rsp = create_response_by_name('GetSelInfo') rsp .entries = 1 rsps.append(rsp) rsp = create_response_by_name('ReserveSel') rsps.append(rsp) rsp = create_response_by_name('GetSelEntry') rsp.record_data = [0, 0, SelEntry.TYPE_SYSTEM_EVENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] rsps.append(rsp) rsp = create_response_by_name('GetSelEntry') rsp.record_data = [0, 0, SelEntry.TYPE_SYSTEM_EVENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] rsp.next_record_id = 0xffff rsps.append(rsp) mock_send_message = MagicMock(side_effect=rsps) interface = interfaces.create_interface('mock') ipmi = create_connection(interface) ipmi.send_message = mock_send_message entries = ipmi.get_sel_entries() assert len(entries) == 2 class TestSelInfo(object): rsp = create_response_by_name('GetSelInfo') rsp.version = 1 rsp.entries = 1023 rsp.free_bytes = 512 info = SelInfo(rsp) assert info.version == 1 assert info.entries == 1023 assert info.free_bytes == 512 rsp.operation_support.get_sel_allocation_info = 1 rsp.operation_support.reserve_sel = 0 rsp.operation_support.partial_add_sel_entry = 0 rsp.operation_support.delete_sel = 0 rsp.operation_support.overflow_flag = 0 info = SelInfo(rsp) assert 'get_sel_allocation_info' in info.operation_support assert 'reserve_sel' not in info.operation_support assert 'partial_add_sel_entry' not in info.operation_support assert 'delete_sel' not in info.operation_support assert 'overflow_flag' not in info.operation_support rsp.operation_support.get_sel_allocation_info = 0 rsp.operation_support.reserve_sel = 1 rsp.operation_support.partial_add_sel_entry = 0 rsp.operation_support.delete_sel = 0 rsp.operation_support.overflow_flag = 0 info = SelInfo(rsp) assert 'get_sel_allocation_info' not in info.operation_support assert 'reserve_sel' in info.operation_support assert 'partial_add_sel_entry' not in info.operation_support assert 'delete_sel' not in info.operation_support assert 'overflow_flag' not in info.operation_support rsp.operation_support.get_sel_allocation_info = 0 rsp.operation_support.reserve_sel = 0 rsp.operation_support.partial_add_sel_entry = 1 rsp.operation_support.delete_sel = 0 rsp.operation_support.overflow_flag = 0 info = SelInfo(rsp) assert 'get_sel_allocation_info' not in info.operation_support assert 'reserve_sel' not in info.operation_support assert 'partial_add_sel_entry' in info.operation_support assert 'delete_sel' not in info.operation_support assert 'overflow_flag' not in info.operation_support rsp.operation_support.get_sel_allocation_info = 0 rsp.operation_support.reserve_sel = 0 rsp.operation_support.partial_add_sel_entry = 0 rsp.operation_support.delete_sel = 1 rsp.operation_support.overflow_flag = 0 info = SelInfo(rsp) assert 'get_sel_allocation_info' not in info.operation_support assert 'reserve_sel' not in info.operation_support assert 'partial_add_sel_entry' not in info.operation_support assert 'delete_sel' in info.operation_support assert 'overflow_flag' not in info.operation_support rsp.operation_support.get_sel_allocation_info = 0 rsp.operation_support.reserve_sel = 0 rsp.operation_support.partial_add_sel_entry = 0 rsp.operation_support.delete_sel = 0 rsp.operation_support.overflow_flag = 1 info = SelInfo(rsp) assert 'get_sel_allocation_info' not in info.operation_support assert 'reserve_sel' not in info.operation_support assert 'partial_add_sel_entry' not in info.operation_support assert 'delete_sel' not in info.operation_support assert 'overflow_flag' in info.operation_support class TestSelEnty(object): def test_from_data(self): data = [0xff, 0x03, 0x02, 0xf7, 0x61, 0xef, 0x52, 0x7e, 0x00, 0x04, 0xf2, 0x09, 0x7f, 0x00, 0xff, 0xff] entry = SelEntry(data) assert entry.type == 2 assert entry.sensor_type == 0xf2 def test_from_data_event_direction(self): data = [0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00] entry = SelEntry(data) assert entry.event_direction == 0 data = [0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00] entry = SelEntry(data) assert entry.event_direction == 1 def test_from_data_event_type(self): data = [0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00] entry = SelEntry(data) assert entry.event_type == 0x7f data = [0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00] entry = SelEntry(data) assert entry.event_type == 0x00 def test_from_data_event_data(self): data = [0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x11, 0x22, 0x33] entry = SelEntry(data) assert entry.event_data[0] == 0x11 assert entry.event_data[1] == 0x22 assert entry.event_data[2] == 0x33 def test_type_to_string(self): assert SelEntry.type_to_string(0) is None assert SelEntry.type_to_string(0x02) == 'System Event' assert SelEntry.type_to_string(0xc0) == 'OEM timestamped (0xc0)' assert SelEntry.type_to_string(0xe0) == 'OEM non-timestamped (0xe0)' python-ipmi-0.5.4/tests/test_sensor.py000066400000000000000000000051431436677633700201120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from mock import MagicMock from pyipmi import interfaces, create_connection from pyipmi.msgs.sensor import SetSensorThresholdsRsp, PlatformEventRsp from pyipmi.sensor import (EVENT_READING_TYPE_SENSOR_SPECIFIC, SENSOR_TYPE_MODULE_HOT_SWAP) def test_set_sensor_thresholds(): rsp = SetSensorThresholdsRsp() rsp.completion_code = 0 mock_send_recv = MagicMock() mock_send_recv.return_value = rsp interface = interfaces.create_interface('mock') ipmi = create_connection(interface) ipmi.send_message = mock_send_recv ipmi.set_sensor_thresholds(sensor_number=5, lun=1) args, _ = mock_send_recv.call_args req = args[0] assert req.lun == 1 assert req.sensor_number == 5 ipmi.set_sensor_thresholds(sensor_number=0, unr=10) args, _ = mock_send_recv.call_args req = args[0] assert req.set_mask.unr == 1 assert req.threshold.unr == 10 assert req.set_mask.ucr == 0 assert req.threshold.ucr == 0 assert req.set_mask.unc == 0 assert req.threshold.unc == 0 assert req.set_mask.lnc == 0 assert req.threshold.lnc == 0 assert req.set_mask.lcr == 0 assert req.threshold.lcr == 0 assert req.set_mask.lnr == 0 assert req.threshold.lnr == 0 ipmi.set_sensor_thresholds(sensor_number=5, ucr=11) args, _ = mock_send_recv.call_args req = args[0] assert req.lun == 0 assert req.set_mask.unr == 0 assert req.threshold.unr == 0 assert req.set_mask.ucr == 1 assert req.threshold.ucr == 11 assert req.set_mask.unc == 0 assert req.threshold.unc == 0 assert req.set_mask.lnc == 0 assert req.threshold.lnc == 0 assert req.set_mask.lcr == 0 assert req.threshold.lcr == 0 assert req.set_mask.lnr == 0 assert req.threshold.lnr == 0 def test_send_platform_event(): rsp = PlatformEventRsp() rsp.completion_code = 0 mock_send_recv = MagicMock() mock_send_recv.return_value = rsp interface = interfaces.create_interface('mock') ipmi = create_connection(interface) ipmi.send_message = mock_send_recv # Module handle closed event ipmi.send_platform_event(SENSOR_TYPE_MODULE_HOT_SWAP, 1, EVENT_READING_TYPE_SENSOR_SPECIFIC, asserted=True, event_data=[0, 0xff, 0xff]) args, _ = mock_send_recv.call_args req = args[0] assert req.event_message_rev == 4 assert req.sensor_type == 0xf2 assert req.sensor_number == 1 assert req.event_type.type == 0x6f assert req.event_type.dir == 0 assert req.event_data == [0, 0xff, 0xff] python-ipmi-0.5.4/tests/test_utils.py000066400000000000000000000055641436677633700177500ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from array import array from pyipmi.utils import ByteBuffer, chunks from pyipmi.errors import DecodingError def test_bytebuffer_init_from_list(): buf = ByteBuffer([0xf8]) assert buf.array == array('B', [0xf8]) def test_bytebuffer_init_from_tuple(): buf = ByteBuffer((0xf8,)) assert buf.array == array('B', [0xf8]) def test_bytebuffer_initi_fromstring(): buf = ByteBuffer(b'\xf8') assert buf.array == array('B', [0xf8]) def test_bytebuffer_push_unsigned_int(): buf = ByteBuffer((1, 0)) buf.push_unsigned_int(255, 1) assert buf[0] == 1 assert buf[1] == 0 assert buf[2] == 255 buf.push_unsigned_int(255, 2) assert buf[3] == 255 assert buf[4] == 0 buf.push_unsigned_int(256, 2) assert buf[5] == 0 assert buf[6] == 1 def test_bytebuffer_pop_unsigned_int(): buf = ByteBuffer((1, 0, 0, 0)) assert buf.pop_unsigned_int(1) == 1 buf = ByteBuffer((0, 1, 0, 0)) assert buf.pop_unsigned_int(2) == 0x100 buf = ByteBuffer((0, 0, 1, 0)) assert buf.pop_unsigned_int(3) == 0x10000 buf = ByteBuffer((0, 0, 0, 1)) assert buf.pop_unsigned_int(4) == 0x1000000 def test_bytebuffer_pop_unsigned_int_error(): with pytest.raises(DecodingError): buf = ByteBuffer((0, 0)) buf.pop_unsigned_int(3) def test_bytebuffer_push_string(): buf = ByteBuffer() buf.push_string(b'0123') assert buf[0] == 0x30 assert buf[1] == 0x31 assert buf[2] == 0x32 assert buf[3] == 0x33 assert buf.tostring() == b'0123' buf = ByteBuffer() buf.push_string(b'\x00\xb4') assert buf.tostring() == b'\x00\xb4' def test_bytebuffer_pop_string(): buf = ByteBuffer(b'\x30\x31\x32\x33') assert buf.pop_string(2) == b'01' assert buf.tostring() == b'23' def test_bytebuffer_tostring(): buf = ByteBuffer(b'\x30\x31\x32\x33') assert buf.tostring() == b'0123' def test_bytebuffer_pop_slice(): buf = ByteBuffer(b'\x30\x31\x32\x33') cut = buf.pop_slice(1) assert buf.tostring() == b'123' assert cut.tostring() == b'0' buf = ByteBuffer(b'\x30\x31\x32\x33') cut = buf.pop_slice(2) assert buf.tostring() == b'23' assert cut.tostring() == b'01' buf = ByteBuffer(b'\x30\x31\x32\x33') cut = buf.pop_slice(3) assert buf.tostring() == b'3' assert cut.tostring() == b'012' buf = ByteBuffer(b'\x30\x31\x32\x33') cut = buf.pop_slice(4) assert buf.tostring() == b'' assert cut.tostring() == b'0123' def test_bytebuffer_pop_slice_error(): with pytest.raises(DecodingError): buf = ByteBuffer(b'\x30\x31\x32\x33') buf.pop_slice(5) def test_chunks(): data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] result = list() for chunk in chunks(data, 2): result.extend(chunk) assert len(chunk) == 2 assert result == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]