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.
- {python_ipmi-0.6.0/python_ipmi.egg-info → python_ipmi-0.6.1}/PKG-INFO +66 -6
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/README.md +64 -4
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/__init__.py +184 -19
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/bmc.py +6 -6
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/chassis.py +1 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/dcmi.py +32 -32
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/fields.py +8 -2
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/fru.py +8 -8
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/hpm.py +24 -16
- python_ipmi-0.6.1/pyipmi/interfaces/__init__.py +111 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/aardvark.py +61 -0
- python_ipmi-0.6.1/pyipmi/interfaces/base.py +179 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmb.py +204 -58
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmbdev.py +30 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmidev.py +81 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/ipmitool.py +110 -10
- python_ipmi-0.6.1/pyipmi/interfaces/mock.py +47 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/openipmblink.py +116 -5
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/rmcp.py +285 -23
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/rmcpplus.py +173 -8
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/interfaces/router.py +87 -10
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/ipmitool.py +17 -11
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/mixin.py +2 -2
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/message.py +5 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sensor.py +19 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/picmg.py +32 -32
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sdr.py +17 -13
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sel.py +8 -8
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/sensor.py +23 -10
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/utils.py +4 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/version.py +1 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/vita.py +20 -20
- {python_ipmi-0.6.0 → python_ipmi-0.6.1/python_ipmi.egg-info}/PKG-INFO +66 -6
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_dcmi.py +20 -20
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_fields.py +22 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_fru.py +1 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_hpm.py +25 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_ipmi.py +86 -5
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_ipmitool.py +58 -2
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sdr.py +44 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sensor.py +25 -1
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_utils.py +15 -1
- python_ipmi-0.6.0/pyipmi/interfaces/__init__.py +0 -48
- python_ipmi-0.6.0/pyipmi/interfaces/base.py +0 -87
- python_ipmi-0.6.0/pyipmi/interfaces/mock.py +0 -26
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/AUTHORS +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/COPYING +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/MANIFEST.in +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/man/ipmitool.py.1 +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/constants.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/errors.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/event.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/helper.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/lan.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/logger.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/messaging.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/__init__.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/bmc.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/chassis.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/constants.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/dcmi.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/device_messaging.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/event.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/fru.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/hpm.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/lan.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/picmg.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/registry.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sdr.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/sel.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/session.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/msgs/vita.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/session.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/pyipmi/state.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/SOURCES.txt +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/dependency_links.txt +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/entry_points.txt +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/requires.txt +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/python_ipmi.egg-info/top_level.txt +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/setup.cfg +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/setup.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_bmc.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_chassis.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_constants.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_device_messaging.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_errors.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_event.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_helper.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_lan.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_messaging.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_picmg.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_sel.py +0 -0
- {python_ipmi-0.6.0 → python_ipmi-0.6.1}/tests/test_session.py +0 -0
- {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.
|
|
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.
|
|
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
|
[](https://github.com/kontron/python-ipmi/actions/workflows/test.yml)
|
|
46
|
-
[](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml)
|
|
47
|
+
[](https://pypi.org/project/python-ipmi/)
|
|
47
48
|
[](https://python-ipmi.readthedocs.io/en/latest/?badge=latest)
|
|
48
|
-
[](
|
|
49
|
+
[](https://pypi.org/project/python-ipmi/)
|
|
50
|
+
[](https://github.com/kontron/python-ipmi/blob/master/COPYING)
|
|
51
|
+
[](https://pypistats.org/packages/python-ipmi)
|
|
49
52
|
[](https://coveralls.io/github/kontron/python-ipmi?branch=master)
|
|
50
53
|
[](http://codeclimate.com/github/kontron/python-ipmi)
|
|
51
54
|
[](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
|
|
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
|
[](https://github.com/kontron/python-ipmi/actions/workflows/test.yml)
|
|
4
|
-
[](https://github.com/kontron/python-ipmi/actions/workflows/lint.yml)
|
|
5
|
+
[](https://pypi.org/project/python-ipmi/)
|
|
5
6
|
[](https://python-ipmi.readthedocs.io/en/latest/?badge=latest)
|
|
6
|
-
[](
|
|
7
|
+
[](https://pypi.org/project/python-ipmi/)
|
|
8
|
+
[](https://github.com/kontron/python-ipmi/blob/master/COPYING)
|
|
9
|
+
[](https://pypistats.org/packages/python-ipmi)
|
|
7
10
|
[](https://coveralls.io/github/kontron/python-ipmi?branch=master)
|
|
8
11
|
[](http://codeclimate.com/github/kontron/python-ipmi)
|
|
9
12
|
[](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
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
151
|
+
"""Initialize the target.
|
|
103
152
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
216
|
-
|
|
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.
|
|
324
|
+
if self.is_target_accessible():
|
|
325
|
+
return
|
|
221
326
|
except IpmiTimeoutError:
|
|
222
|
-
|
|
327
|
+
pass
|
|
328
|
+
time.sleep(interval)
|
|
329
|
+
|
|
330
|
+
self.is_target_accessible()
|
|
223
331
|
|
|
224
|
-
|
|
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
|
|
245
|
-
|
|
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
|
|
256
|
-
|
|
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
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|