python-ipmi 0.6.0__tar.gz → 0.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. {python_ipmi-0.6.0/python_ipmi.egg-info → python_ipmi-0.6.1}/PKG-INFO +66 -6
  2. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/README.md +64 -4
  3. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/__init__.py +184 -19
  4. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/bmc.py +6 -6
  5. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/chassis.py +1 -1
  6. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/dcmi.py +32 -32
  7. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/fields.py +8 -2
  8. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/fru.py +8 -8
  9. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/hpm.py +24 -16
  10. python_ipmi-0.6.1/pyipmi/interfaces/__init__.py +111 -0
  11. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/aardvark.py +61 -0
  12. python_ipmi-0.6.1/pyipmi/interfaces/base.py +179 -0
  13. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmb.py +204 -58
  14. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmbdev.py +30 -1
  15. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmidev.py +81 -1
  16. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmitool.py +110 -10
  17. python_ipmi-0.6.1/pyipmi/interfaces/mock.py +47 -0
  18. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/openipmblink.py +116 -5
  19. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/rmcp.py +285 -23
  20. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/rmcpplus.py +173 -8
  21. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/router.py +87 -10
  22. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/ipmitool.py +17 -11
  23. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/mixin.py +2 -2
  24. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/message.py +5 -1
  25. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sensor.py +19 -0
  26. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/picmg.py +32 -32
  27. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sdr.py +17 -13
  28. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sel.py +8 -8
  29. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sensor.py +23 -10
  30. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/utils.py +4 -1
  31. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/version.py +1 -1
  32. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/vita.py +20 -20
  33. {python_ipmi-0.6.0 → python_ipmi-0.6.1/python_ipmi.egg-info}/PKG-INFO +66 -6
  34. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_dcmi.py +20 -20
  35. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_fields.py +22 -0
  36. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_fru.py +1 -1
  37. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_hpm.py +25 -0
  38. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_ipmi.py +86 -5
  39. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_ipmitool.py +58 -2
  40. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sdr.py +44 -0
  41. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sensor.py +25 -1
  42. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_utils.py +15 -1
  43. python_ipmi-0.6.0/pyipmi/interfaces/__init__.py +0 -48
  44. python_ipmi-0.6.0/pyipmi/interfaces/base.py +0 -87
  45. python_ipmi-0.6.0/pyipmi/interfaces/mock.py +0 -26
  46. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/AUTHORS +0 -0
  47. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/COPYING +0 -0
  48. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/MANIFEST.in +0 -0
  49. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/man/ipmitool.py.1 +0 -0
  50. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/constants.py +0 -0
  51. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/errors.py +0 -0
  52. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/event.py +0 -0
  53. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/helper.py +0 -0
  54. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/lan.py +0 -0
  55. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/logger.py +0 -0
  56. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/messaging.py +0 -0
  57. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/__init__.py +0 -0
  58. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/bmc.py +0 -0
  59. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/chassis.py +0 -0
  60. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/constants.py +0 -0
  61. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/dcmi.py +0 -0
  62. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/device_messaging.py +0 -0
  63. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/event.py +0 -0
  64. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/fru.py +0 -0
  65. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/hpm.py +0 -0
  66. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/lan.py +0 -0
  67. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/picmg.py +0 -0
  68. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/registry.py +0 -0
  69. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sdr.py +0 -0
  70. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sel.py +0 -0
  71. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/session.py +0 -0
  72. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/vita.py +0 -0
  73. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/session.py +0 -0
  74. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/state.py +0 -0
  75. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/SOURCES.txt +0 -0
  76. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/dependency_links.txt +0 -0
  77. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/entry_points.txt +0 -0
  78. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/requires.txt +0 -0
  79. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/top_level.txt +0 -0
  80. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/setup.cfg +0 -0
  81. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/setup.py +0 -0
  82. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_bmc.py +0 -0
  83. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_chassis.py +0 -0
  84. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_constants.py +0 -0
  85. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_device_messaging.py +0 -0
  86. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_errors.py +0 -0
  87. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_event.py +0 -0
  88. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_helper.py +0 -0
  89. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_lan.py +0 -0
  90. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_messaging.py +0 -0
  91. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_picmg.py +0 -0
  92. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sel.py +0 -0
  93. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_session.py +0 -0
  94. {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_vita.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-ipmi
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Pure python IPMI library
5
5
  Home-page: https://github.com/kontron/python-ipmi
6
- Download-URL: https://github.com/kontron/python-ipmi/tarball/0.6.0
6
+ Download-URL: https://github.com/kontron/python-ipmi/tarball/0.6.1
7
7
  Author: Michael Walle, Heiko Thiery
8
8
  Author-email: michael.walle@kontron.com, heiko.thiery@kontron.com
9
9
  License: LGPLv2+
@@ -43,9 +43,12 @@ Dynamic: summary
43
43
  # Pure Python IPMI Library
44
44
 
45
45
  [![Build Status](https://github.com/kontron/python-ipmi/actions/workflows/test.yml/badge.svg)](https://github.com/kontron/python-ipmi/actions/workflows/test.yml)
46
- [![PyPI version](https://badge.fury.io/py/python-ipmi.svg)](http://badge.fury.io/py/python-ipmi)
46
+ [![Lint](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml/badge.svg)](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml)
47
+ [![PyPI version](https://img.shields.io/pypi/v/python-ipmi.svg)](https://pypi.org/project/python-ipmi/)
47
48
  [![Documentation Status](https://readthedocs.org/projects/python-ipmi/badge/?version=latest)](https://python-ipmi.readthedocs.io/en/latest/?badge=latest)
48
- [![Python versions](https://img.shields.io/pypi/pyversions/python-ipmi.svg)](http://badge.fury.io/py/python-ipmi)
49
+ [![Python versions](https://img.shields.io/pypi/pyversions/python-ipmi.svg)](https://pypi.org/project/python-ipmi/)
50
+ [![License](https://img.shields.io/pypi/l/python-ipmi.svg)](https://github.com/kontron/python-ipmi/blob/master/COPYING)
51
+ [![Downloads](https://img.shields.io/pypi/dm/python-ipmi.svg)](https://pypistats.org/packages/python-ipmi)
49
52
  [![Coverage Status](https://coveralls.io/repos/github/kontron/python-ipmi/badge.svg?branch=master)](https://coveralls.io/github/kontron/python-ipmi?branch=master)
50
53
  [![Code Climate](https://codeclimate.com/github/kontron/python-ipmi/badges/gpa.svg)](http://codeclimate.com/github/kontron/python-ipmi)
51
54
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/068eca4b1e784425aa46ae0b06aeaf37)](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)
@@ -58,7 +61,7 @@ Dynamic: summary
58
61
  * RMCP+ interface
59
62
  * native
60
63
  * legacy using [ipmitool] as backend
61
- * system interface (using ipmitool as backend)
64
+ * system interface
62
65
  * native (KCS, SMIC, BT, SSIF) using the IPMI driver on Linux
63
66
  * legacy using [ipmitool] as backend
64
67
  * IPMB interface
@@ -89,7 +92,8 @@ For the native system interface the Linux IPMI driver is needed
89
92
  https://www.kernel.org/doc/html/latest/driver-api/ipmi.html
90
93
 
91
94
  For legacy RMCP, RMCP+ and system interface (KCS) using ipmitool as backend
92
- the installation of ipmitool is required.
95
+ the installation of ipmitool is required. Any ipmitool 1.8.x works; the
96
+ serial interface (`serial-terminal`) needs at least version 1.8.13.
93
97
 
94
98
  The native RMCP+ interface needs the [cryptography] package for
95
99
  encrypted sessions (AES-CBC-128, cipher suites 3 and 17):
@@ -117,6 +121,61 @@ a temporary location and install:
117
121
  python setup.py install
118
122
  ```
119
123
 
124
+ ### Running from the source tree
125
+
126
+ To work on the library or to use it directly from a git checkout, create a
127
+ virtual environment and install the checkout in editable mode. Changes to the
128
+ source are then active without reinstalling:
129
+
130
+ ```shell
131
+ git clone https://github.com/kontron/python-ipmi.git
132
+ cd python-ipmi
133
+ python3 -m venv .venv
134
+ . .venv/bin/activate
135
+ pip install -e '.[rmcpplus]'
136
+ ```
137
+
138
+ This also installs the `ipmitool.py` command line tool and generates
139
+ `pyipmi/version.py` with the version from `git describe`. Install the
140
+ optional packages for the interfaces you need: `pyserial` for the
141
+ openipmblink interface and `pyaardvark` for the Aardvark IPMB interface.
142
+
143
+ To run the tests:
144
+
145
+ ```shell
146
+ pip install pytest
147
+ pytest
148
+ ```
149
+
150
+ The CI also runs the linter [ruff], the type checker mypy and the spell
151
+ checker codespell. They are configured in `ruff.toml` and `setup.cfg`, so
152
+ they run without arguments. Use the versions pinned in
153
+ `.github/workflows/lint.yml` to get the same results as the CI:
154
+
155
+ ```shell
156
+ pip install ruff==0.16.10 mypy==2.4.0 codespell
157
+ ruff check .
158
+ mypy
159
+ codespell
160
+ ```
161
+
162
+ `ruff check --fix .` fixes many of the reported issues automatically.
163
+
164
+ Alternatively, the checkout can be used without installing by adding its top
165
+ directory to `PYTHONPATH`. Then `pyipmi` can be imported from any directory,
166
+ e.g. by your own scripts or the ones in `examples/`, and the tool is started
167
+ with `python3 -m pyipmi.ipmitool`:
168
+
169
+ ```shell
170
+ export PYTHONPATH=/path/to/python-ipmi
171
+ python3 -m pyipmi.ipmitool -V
172
+ ```
173
+
174
+ The optional packages have to be installed separately then, e.g.
175
+ `pip install cryptography` for encrypted RMCP+ sessions. The version is shown
176
+ as `dev` as long as `pyipmi/version.py` has not been generated, e.g. by
177
+ `python3 setup.py --version`.
178
+
120
179
  ### Package version
121
180
 
122
181
  The version of the package is taken from, in this order:
@@ -310,3 +369,4 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
310
369
  [Total Phase]: http://www.totalphase.com
311
370
  [ipmitool]: https://codeberg.org/IPMITool/ipmitool
312
371
  [cryptography]: https://pypi.org/project/cryptography/
372
+ [ruff]: https://docs.astral.sh/ruff/
@@ -1,9 +1,12 @@
1
1
  # Pure Python IPMI Library
2
2
 
3
3
  [![Build Status](https://github.com/kontron/python-ipmi/actions/workflows/test.yml/badge.svg)](https://github.com/kontron/python-ipmi/actions/workflows/test.yml)
4
- [![PyPI version](https://badge.fury.io/py/python-ipmi.svg)](http://badge.fury.io/py/python-ipmi)
4
+ [![Lint](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml/badge.svg)](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml)
5
+ [![PyPI version](https://img.shields.io/pypi/v/python-ipmi.svg)](https://pypi.org/project/python-ipmi/)
5
6
  [![Documentation Status](https://readthedocs.org/projects/python-ipmi/badge/?version=latest)](https://python-ipmi.readthedocs.io/en/latest/?badge=latest)
6
- [![Python versions](https://img.shields.io/pypi/pyversions/python-ipmi.svg)](http://badge.fury.io/py/python-ipmi)
7
+ [![Python versions](https://img.shields.io/pypi/pyversions/python-ipmi.svg)](https://pypi.org/project/python-ipmi/)
8
+ [![License](https://img.shields.io/pypi/l/python-ipmi.svg)](https://github.com/kontron/python-ipmi/blob/master/COPYING)
9
+ [![Downloads](https://img.shields.io/pypi/dm/python-ipmi.svg)](https://pypistats.org/packages/python-ipmi)
7
10
  [![Coverage Status](https://coveralls.io/repos/github/kontron/python-ipmi/badge.svg?branch=master)](https://coveralls.io/github/kontron/python-ipmi?branch=master)
8
11
  [![Code Climate](https://codeclimate.com/github/kontron/python-ipmi/badges/gpa.svg)](http://codeclimate.com/github/kontron/python-ipmi)
9
12
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/068eca4b1e784425aa46ae0b06aeaf37)](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)
@@ -16,7 +19,7 @@
16
19
  * RMCP+ interface
17
20
  * native
18
21
  * legacy using [ipmitool] as backend
19
- * system interface (using ipmitool as backend)
22
+ * system interface
20
23
  * native (KCS, SMIC, BT, SSIF) using the IPMI driver on Linux
21
24
  * legacy using [ipmitool] as backend
22
25
  * IPMB interface
@@ -47,7 +50,8 @@ For the native system interface the Linux IPMI driver is needed
47
50
  https://www.kernel.org/doc/html/latest/driver-api/ipmi.html
48
51
 
49
52
  For legacy RMCP, RMCP+ and system interface (KCS) using ipmitool as backend
50
- the installation of ipmitool is required.
53
+ the installation of ipmitool is required. Any ipmitool 1.8.x works; the
54
+ serial interface (`serial-terminal`) needs at least version 1.8.13.
51
55
 
52
56
  The native RMCP+ interface needs the [cryptography] package for
53
57
  encrypted sessions (AES-CBC-128, cipher suites 3 and 17):
@@ -75,6 +79,61 @@ a temporary location and install:
75
79
  python setup.py install
76
80
  ```
77
81
 
82
+ ### Running from the source tree
83
+
84
+ To work on the library or to use it directly from a git checkout, create a
85
+ virtual environment and install the checkout in editable mode. Changes to the
86
+ source are then active without reinstalling:
87
+
88
+ ```shell
89
+ git clone https://github.com/kontron/python-ipmi.git
90
+ cd python-ipmi
91
+ python3 -m venv .venv
92
+ . .venv/bin/activate
93
+ pip install -e '.[rmcpplus]'
94
+ ```
95
+
96
+ This also installs the `ipmitool.py` command line tool and generates
97
+ `pyipmi/version.py` with the version from `git describe`. Install the
98
+ optional packages for the interfaces you need: `pyserial` for the
99
+ openipmblink interface and `pyaardvark` for the Aardvark IPMB interface.
100
+
101
+ To run the tests:
102
+
103
+ ```shell
104
+ pip install pytest
105
+ pytest
106
+ ```
107
+
108
+ The CI also runs the linter [ruff], the type checker mypy and the spell
109
+ checker codespell. They are configured in `ruff.toml` and `setup.cfg`, so
110
+ they run without arguments. Use the versions pinned in
111
+ `.github/workflows/lint.yml` to get the same results as the CI:
112
+
113
+ ```shell
114
+ pip install ruff==0.16.10 mypy==2.4.0 codespell
115
+ ruff check .
116
+ mypy
117
+ codespell
118
+ ```
119
+
120
+ `ruff check --fix .` fixes many of the reported issues automatically.
121
+
122
+ Alternatively, the checkout can be used without installing by adding its top
123
+ directory to `PYTHONPATH`. Then `pyipmi` can be imported from any directory,
124
+ e.g. by your own scripts or the ones in `examples/`, and the tool is started
125
+ with `python3 -m pyipmi.ipmitool`:
126
+
127
+ ```shell
128
+ export PYTHONPATH=/path/to/python-ipmi
129
+ python3 -m pyipmi.ipmitool -V
130
+ ```
131
+
132
+ The optional packages have to be installed separately then, e.g.
133
+ `pip install cryptography` for encrypted RMCP+ sessions. The version is shown
134
+ as `dev` as long as `pyipmi/version.py` has not been generated, e.g. by
135
+ `python3 setup.py --version`.
136
+
78
137
  ### Package version
79
138
 
80
139
  The version of the package is taken from, in this order:
@@ -268,3 +327,4 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
268
327
  [Total Phase]: http://www.totalphase.com
269
328
  [ipmitool]: https://codeberg.org/IPMITool/ipmitool
270
329
  [cryptography]: https://pypi.org/project/cryptography/
330
+ [ruff]: https://docs.astral.sh/ruff/
@@ -14,10 +14,38 @@
14
14
  # License along with this library; if not, write to the Free Software
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
+ """A pure Python IPMI library.
18
+
19
+ A connection to an IPMI device is an :class:`Ipmi` object, created by
20
+ :func:`create_connection` for an interface (see
21
+ :func:`pyipmi.interfaces.create_interface`).
22
+ The connection sends the requests to its :class:`Target`, the BMC or a
23
+ controller behind it, which is reached over the :class:`Routing` hops of
24
+ the target. The IPMI commands are the methods of :class:`Ipmi`, which
25
+ inherits them from the command groups of the modules, e.g.
26
+ :mod:`pyipmi.bmc` and :mod:`pyipmi.sdr`.
27
+
28
+ Example:
29
+ Print the device ID of a BMC over RMCP+::
30
+
31
+ import pyipmi
32
+ import pyipmi.interfaces
33
+
34
+ interface = pyipmi.interfaces.create_interface('rmcpplus')
35
+ ipmi = pyipmi.create_connection(interface)
36
+ ipmi.session.set_session_type_rmcp('10.0.0.1', port=623)
37
+ ipmi.session.set_auth_type_user('admin', 'admin')
38
+ ipmi.target = pyipmi.Target(ipmb_address=0x20)
39
+
40
+ with ipmi:
41
+ print(ipmi.get_device_id())
42
+ """
43
+
17
44
  from __future__ import annotations
18
45
 
19
46
  import time
20
47
  import ast
48
+ import warnings
21
49
  from typing import Any, Literal
22
50
 
23
51
  from . import bmc
@@ -49,6 +77,19 @@ except ImportError:
49
77
 
50
78
 
51
79
  def create_connection(interface: Any) -> Ipmi:
80
+ """Create a connection for an interface.
81
+
82
+ The connection gets a new :class:`pyipmi.session.Session` for the
83
+ interface. The target is not set, assign it to :attr:`Ipmi.target`
84
+ before sending requests.
85
+
86
+ Args:
87
+ interface: The interface, e.g. created by
88
+ :func:`pyipmi.interfaces.create_interface`.
89
+
90
+ Returns:
91
+ The connection.
92
+ """
52
93
  session = Session()
53
94
  session.interface = interface
54
95
  return Ipmi(interface=interface, session=session)
@@ -78,9 +119,17 @@ class NullRequester:
78
119
 
79
120
 
80
121
  class Routing:
81
- """The Target class represents an IPMI target."""
122
+ """One hop of the path to a target, see :meth:`Target.set_routing`."""
82
123
 
83
124
  def __init__(self, rq_sa: int, rs_sa: int, channel: int | None) -> None:
125
+ """Initialize the hop.
126
+
127
+ Args:
128
+ rq_sa: The requester slave address.
129
+ rs_sa: The responder slave address.
130
+ channel: The channel of the bridge to the next hop, None for
131
+ the last hop.
132
+ """
84
133
  self.rq_sa = rq_sa
85
134
  self.rs_sa = rs_sa
86
135
  self.channel = channel
@@ -99,11 +148,14 @@ class Target:
99
148
 
100
149
  def __init__(self, ipmb_address: int | None = None,
101
150
  routing: str | list[tuple] | None = None) -> None:
102
- """Initializer for the Target class.
151
+ """Initialize the target.
103
152
 
104
- `ipmb_address` is the IPMB target address
105
- `routing` is the bridging information used to build send message
106
- commands.
153
+ Args:
154
+ ipmb_address: The IPMB address of the target, e.g. 0x20 for
155
+ the BMC.
156
+ routing: The path over which the target is reachable, used to
157
+ build the bridged Send Message requests, see
158
+ :meth:`set_routing`.
107
159
  """
108
160
  if ipmb_address:
109
161
  self.ipmb_address = ipmb_address
@@ -112,6 +164,10 @@ class Target:
112
164
  self.set_routing(routing)
113
165
 
114
166
  def set_routing_information(self, routing: str | list[tuple]) -> None:
167
+ """Set the path over which a target is reachable.
168
+
169
+ An alias of :meth:`set_routing`.
170
+ """
115
171
  self.set_routing(routing)
116
172
 
117
173
  def set_routing(self, routing: str | list[tuple]) -> None:
@@ -171,10 +227,32 @@ class Target:
171
227
  class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
172
228
  sdr.Sdr, sensor.Sensor, event.Event, sel.Sel, lan.Lan,
173
229
  messaging.Messaging, vita.Vita):
230
+ """A connection to an IPMI device.
231
+
232
+ The IPMI commands are the methods of this class, which it inherits
233
+ from the command groups, e.g. :meth:`get_device_id` from the one of
234
+ :mod:`pyipmi.bmc`. The requests are sent over the interface to
235
+ the target.
236
+
237
+ The connection is a context manager, which opens the interface and
238
+ establishes the session on entry and closes them on exit. Set up the
239
+ session before, see the example of :mod:`pyipmi`.
240
+ """
174
241
 
175
242
  def __init__(self, interface: Any = None, target: Target | None = None,
176
243
  session: Session | None = None,
177
244
  requester: Any = None) -> None:
245
+ """Initialize the connection.
246
+
247
+ Args:
248
+ interface: The interface the requests are sent over.
249
+ target: The target of the requests.
250
+ session: The session, a new one if not given. The interface
251
+ of the session is set to ``interface``.
252
+ requester: The requester of the requests, needed by interfaces
253
+ that send the requests on the IPMB, a
254
+ :class:`NullRequester` if not given.
255
+ """
178
256
  self._interface = interface
179
257
 
180
258
  # we need a session, set if not passed
@@ -200,30 +278,83 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
200
278
  return False
201
279
 
202
280
  def open(self) -> None:
281
+ """Open the interface and establish the session."""
203
282
  self.interface.open()
204
283
  if self.session is not None:
205
284
  self.session.establish()
206
285
 
207
286
  def close(self) -> None:
287
+ """Close the session and the interface."""
208
288
  if self.session is not None:
209
289
  self.session.close()
210
290
  self.interface.close()
211
291
 
292
+ def is_target_accessible(self) -> bool:
293
+ """Check if the target answers.
294
+
295
+ Returns:
296
+ True if the target answers. Depending on the interface, False
297
+ is returned or an exception is raised if it does not.
298
+ """
299
+ return self.interface.is_target_accessible(self.target)
300
+
212
301
  def is_ipmc_accessible(self) -> bool:
213
- return self.interface.is_ipmc_accessible(self.target)
302
+ """Deprecated, the old name of :meth:`is_target_accessible`."""
303
+ warnings.warn('is_ipmc_accessible is deprecated, use '
304
+ 'is_target_accessible', DeprecationWarning,
305
+ stacklevel=2)
306
+ return self.is_target_accessible()
214
307
 
215
- def wait_until_ipmb_is_accessible(self, timeout: float,
216
- interval: float = 0.25) -> None:
308
+ def wait_until_target_is_accessible(self, timeout: float,
309
+ interval: float = 0.25) -> None:
310
+ """Wait until the target is accessible.
311
+
312
+ Args:
313
+ timeout: The time to wait in seconds.
314
+ interval: The time between the checks in seconds.
315
+
316
+ Raises:
317
+ IpmiTimeoutError: The target is not accessible after the
318
+ timeout, if the interface raises it, see
319
+ :meth:`is_target_accessible`.
320
+ """
217
321
  start_time = time.time()
218
322
  while time.time() < start_time + (timeout):
219
323
  try:
220
- self.is_ipmc_accessible()
324
+ if self.is_target_accessible():
325
+ return
221
326
  except IpmiTimeoutError:
222
- time.sleep(interval)
327
+ pass
328
+ time.sleep(interval)
329
+
330
+ self.is_target_accessible()
223
331
 
224
- self.is_ipmc_accessible()
332
+ def wait_until_ipmb_is_accessible(self, timeout: float,
333
+ interval: float = 0.25) -> None:
334
+ """Deprecated, use :meth:`wait_until_target_is_accessible`."""
335
+ warnings.warn('wait_until_ipmb_is_accessible is deprecated, use '
336
+ 'wait_until_target_is_accessible', DeprecationWarning,
337
+ stacklevel=2)
338
+ self.wait_until_target_is_accessible(timeout, interval)
225
339
 
226
340
  def send_message(self, req: Message, retry: int = 3) -> Message:
341
+ """Send a request to the target and return the response.
342
+
343
+ The request is sent again if the target is busy. The completion
344
+ code of the response is not checked.
345
+
346
+ Args:
347
+ req: The request message.
348
+ retry: The number of tries.
349
+
350
+ Returns:
351
+ The response message.
352
+
353
+ Raises:
354
+ RetryError: The target is still busy after ``retry`` tries.
355
+ CompletionCodeError: The interface failed with another
356
+ completion code, e.g. of a bridged Send Message request.
357
+ """
227
358
  req.target = self.target
228
359
  req.requester = self.requester
229
360
  rsp = None
@@ -236,13 +367,28 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
236
367
  except CompletionCodeError as e:
237
368
  if e.cc == msgs.constants.CC_NODE_BUSY:
238
369
  continue
370
+ raise
239
371
  else:
240
372
  raise RetryError()
241
373
 
242
374
  return rsp
243
375
 
244
- def send_message_with_name(self, name: str, *args: Any,
245
- **kwargs: Any) -> Message:
376
+ def send_message_by_name(self, name: str, *args: Any,
377
+ **kwargs: Any) -> Message:
378
+ """Send a request by its name and return the response.
379
+
380
+ Args:
381
+ name: The name of the request, e.g. ``'GetDeviceId'``.
382
+ *args: Not used.
383
+ **kwargs: The fields of the request, set as attributes.
384
+
385
+ Returns:
386
+ The response message.
387
+
388
+ Raises:
389
+ CompletionCodeError: The completion code of the response is
390
+ not successful.
391
+ """
246
392
  req = create_request_by_name(name)
247
393
 
248
394
  for key, value in kwargs.items():
@@ -252,20 +398,37 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
252
398
  check_rsp_completion_code(rsp)
253
399
  return rsp
254
400
 
255
- def raw_command(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
256
- """Send the raw command data and return the raw response.
401
+ def send_message_with_name(self, name: str, *args: Any,
402
+ **kwargs: Any) -> Message:
403
+ """Deprecated, the old name of :meth:`send_message_by_name`."""
404
+ warnings.warn('send_message_with_name is deprecated, use '
405
+ 'send_message_by_name', DeprecationWarning,
406
+ stacklevel=2)
407
+ return self.send_message_by_name(name, *args, **kwargs)
257
408
 
258
- lun: the logical unit number
259
- netfn: the network function
260
- raw_bytes: the raw message as bytestring
409
+ def send_raw(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
410
+ """Send a raw request to the target and return the raw response.
261
411
 
262
- Returns the response as bytestring.
412
+ Args:
413
+ lun: The logical unit number.
414
+ netfn: The network function.
415
+ raw_bytes: The request, starting with the command ID.
416
+
417
+ Returns:
418
+ The response, starting with the completion code.
263
419
  """
264
420
  return self.interface.send_and_receive_raw(self.target, lun, netfn,
265
421
  raw_bytes)
266
422
 
423
+ def raw_command(self, lun: int, netfn: int, raw_bytes: bytes) -> bytes:
424
+ """Deprecated, the old name of :meth:`send_raw`."""
425
+ warnings.warn('raw_command is deprecated, use send_raw',
426
+ DeprecationWarning, stacklevel=2)
427
+ return self.send_raw(lun, netfn, raw_bytes)
428
+
267
429
  @property
268
430
  def interface(self) -> Any:
431
+ """The interface the requests are sent over."""
269
432
  try:
270
433
  return self._interface
271
434
  except AttributeError:
@@ -277,6 +440,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
277
440
 
278
441
  @property
279
442
  def session(self) -> Session:
443
+ """The session of the connection."""
280
444
  try:
281
445
  return self._session
282
446
  except AttributeError:
@@ -288,6 +452,7 @@ class Ipmi(bmc.Bmc, chassis.Chassis, dcmi.Dcmi, fru.Fru, picmg.Picmg, hpm.Hpm,
288
452
 
289
453
  @property
290
454
  def target(self) -> Target | None:
455
+ """The target of the requests."""
291
456
  try:
292
457
  return self._target
293
458
  except AttributeError:
@@ -48,7 +48,7 @@ class Bmc(IpmiMixin):
48
48
  The device ID, firmware version, IPMI version and supported
49
49
  functions of the controller.
50
50
  """
51
- return DeviceId(self.send_message_with_name('GetDeviceId'))
51
+ return DeviceId(self.send_message_by_name('GetDeviceId'))
52
52
 
53
53
  def get_device_guid(self) -> DeviceGuid:
54
54
  """Get the GUID of the controller.
@@ -56,15 +56,15 @@ class Bmc(IpmiMixin):
56
56
  Returns:
57
57
  The device GUID.
58
58
  """
59
- return DeviceGuid(self.send_message_with_name('GetDeviceGuid'))
59
+ return DeviceGuid(self.send_message_by_name('GetDeviceGuid'))
60
60
 
61
61
  def cold_reset(self) -> None:
62
62
  """Cold reset the controller, it is reinitialized."""
63
- self.send_message_with_name('ColdReset')
63
+ self.send_message_by_name('ColdReset')
64
64
 
65
65
  def warm_reset(self) -> None:
66
66
  """Warm reset the controller, its state is kept."""
67
- self.send_message_with_name('WarmReset')
67
+ self.send_message_by_name('WarmReset')
68
68
 
69
69
  def i2c_write_read(self, bus_type: int, bus_id: int, channel: int,
70
70
  address: int, count: int,
@@ -169,7 +169,7 @@ class Bmc(IpmiMixin):
169
169
  Returns:
170
170
  The watchdog timer, ``dont_stop`` is not set.
171
171
  """
172
- return Watchdog(self.send_message_with_name('GetWatchdogTimer'))
172
+ return Watchdog(self.send_message_by_name('GetWatchdogTimer'))
173
173
 
174
174
  def reset_watchdog_timer(self) -> None:
175
175
  """Start or restart the watchdog timer with its initial countdown.
@@ -177,7 +177,7 @@ class Bmc(IpmiMixin):
177
177
  Raises:
178
178
  CompletionCodeError: The timer was not set before (0x80).
179
179
  """
180
- self.send_message_with_name('ResetWatchdogTimer')
180
+ self.send_message_by_name('ResetWatchdogTimer')
181
181
 
182
182
 
183
183
  class Watchdog(State):
@@ -207,7 +207,7 @@ class Chassis(IpmiMixin):
207
207
  Returns:
208
208
  The chassis status.
209
209
  """
210
- return ChassisStatus(self.send_message_with_name('GetChassisStatus'))
210
+ return ChassisStatus(self.send_message_by_name('GetChassisStatus'))
211
211
 
212
212
  def chassis_control(self, option: int) -> None:
213
213
  """Control the chassis power.