robotframework-ibmmq 0.1.2__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.
- robotframework_ibmmq-0.1.2/PKG-INFO +172 -0
- robotframework_ibmmq-0.1.2/README.md +142 -0
- robotframework_ibmmq-0.1.2/pyproject.toml +50 -0
- robotframework_ibmmq-0.1.2/src/IBMMQLibrary/IBMMQLibrary.py +320 -0
- robotframework_ibmmq-0.1.2/src/IBMMQLibrary/__init__.py +3 -0
- robotframework_ibmmq-0.1.2/src/IBMMQLibrary/exceptions.py +24 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robotframework-ibmmq
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A Robotframework library for IBM MQ
|
|
5
|
+
Keywords: robotframework,ibm,mqlibrary
|
|
6
|
+
Author: Niels Janssen
|
|
7
|
+
Author-email: Niels Janssen <niels.janssen@uwv.nl>
|
|
8
|
+
License-Expression: EUPL-1.2
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Testing
|
|
12
|
+
Classifier: Framework :: Robot Framework
|
|
13
|
+
Classifier: Framework :: Robot Framework :: Library
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Requires-Dist: robotframework>=7.3.0,<8.3.0
|
|
17
|
+
Requires-Dist: ibmmq>=2.0.0,<3.0.0
|
|
18
|
+
Requires-Dist: twine ; extra == 'deploy'
|
|
19
|
+
Requires-Dist: build ; extra == 'deploy'
|
|
20
|
+
Requires-Dist: wheel ; extra == 'deploy'
|
|
21
|
+
Maintainer: Niels Janssen
|
|
22
|
+
Maintainer-email: Niels Janssen <niels.janssen@uwv.nl>
|
|
23
|
+
Requires-Python: >=3.9.0
|
|
24
|
+
Project-URL: Homepage, https://github.com/nluwv/robotframework-ibmmq
|
|
25
|
+
Project-URL: Documentation, https://robotframework-ibmmq.netlify.app/IBMMQLibrary.html
|
|
26
|
+
Project-URL: Repository, https://github.com/nluwv/robotframework-ibmmq.git
|
|
27
|
+
Project-URL: Issues, https://github.com/nluwv/robotframework-ibmmq/issues
|
|
28
|
+
Provides-Extra: deploy
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# robotframework-ibmmq
|
|
32
|
+
|
|
33
|
+
**Robot Framework keywords for IBM MQ — powered by the `ibmmq` Python package.**
|
|
34
|
+
|
|
35
|
+
This library is a **thin, explicit Robot Framework wrapper around the `ibmmq` Python package**.
|
|
36
|
+
It does **not** abstract away IBM MQ complexity — and that is **by design**.
|
|
37
|
+
|
|
38
|
+
If you use this library, you are using **real IBM MQ**, with **real native dependencies**, exactly like production.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## What this library is (and is not)
|
|
43
|
+
|
|
44
|
+
✅ **IS**
|
|
45
|
+
- A Robot Framework library for **connecting to IBM MQ**
|
|
46
|
+
- A wrapper around the **`ibmmq` Python package**
|
|
47
|
+
- Designed for **real integration testing**, not mocks or file-based substitutes
|
|
48
|
+
|
|
49
|
+
❌ **IS NOT**
|
|
50
|
+
- A pure-Python library
|
|
51
|
+
- A “just pip install and go” solution
|
|
52
|
+
- A fake or simulated MQ implementation
|
|
53
|
+
|
|
54
|
+
If you are looking for something that avoids native dependencies, **this library is not for you**.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 🚨 Read this first: IBM MQ prerequisites are mandatory
|
|
59
|
+
|
|
60
|
+
This library **directly depends on `ibmmq`**, which is a **Python extension module backed by IBM MQ native libraries**.
|
|
61
|
+
|
|
62
|
+
> **If `ibmmq` does not work on your machine, this Robot Framework library will not work either.**
|
|
63
|
+
|
|
64
|
+
Before you do *anything else*, you **must** read and follow the official `ibmmq` prerequisites:
|
|
65
|
+
|
|
66
|
+
👉 **Official ibmmq prerequisites (REQUIRED):**
|
|
67
|
+
https://github.com/ibm-messaging/mq-mqi-python?tab=readme-ov-file#prerequisites
|
|
68
|
+
|
|
69
|
+
### In practical terms, this means:
|
|
70
|
+
|
|
71
|
+
You **must have**, on the machine where tests run:
|
|
72
|
+
|
|
73
|
+
- ✅ IBM MQ C Client
|
|
74
|
+
- ✅ IBM MQ SDK
|
|
75
|
+
- ✅ A working C/C++ build toolchain
|
|
76
|
+
- ✅ Correct environment variables (e.g. `MQ_FILE_PATH` if MQ is not installed in the default location)
|
|
77
|
+
|
|
78
|
+
If any of the above is missing, you will see errors such as:
|
|
79
|
+
|
|
80
|
+
- `ModuleNotFoundError: No module named 'ibmmqc'`
|
|
81
|
+
- DLL load failures
|
|
82
|
+
- Import errors during `pip install` or at runtime
|
|
83
|
+
|
|
84
|
+
These are **environment issues**, not bugs in this library.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Platform notes
|
|
89
|
+
|
|
90
|
+
- ✅ Windows: Supported
|
|
91
|
+
- ✅ Linux: Supported
|
|
92
|
+
- ❌ No native MQ installation = no support
|
|
93
|
+
|
|
94
|
+
On Windows, a proper **Visual C++ build environment is required**, because `ibmmq` includes native extensions.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
### Step 1 — Verify `ibmmq` works first (strongly recommended)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python -c "import ibmmq; print('ibmmq OK')"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If this fails, **stop here** and fix your IBM MQ / `ibmmq` installation first.
|
|
107
|
+
|
|
108
|
+
### Step 2 — Install this library
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install robotframework-ibmmq
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
No magic. No hidden installers.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Usage example
|
|
119
|
+
|
|
120
|
+
```robot
|
|
121
|
+
*** Settings ***
|
|
122
|
+
Library MQLibrary
|
|
123
|
+
|
|
124
|
+
*** Test Cases ***
|
|
125
|
+
Connect to IBM MQ
|
|
126
|
+
Connect MQ
|
|
127
|
+
... queue_manager=QM_EXAMPLE
|
|
128
|
+
... hostname=mq.example.internal
|
|
129
|
+
... port=1414
|
|
130
|
+
... channel=SYSTEM.ADMIN.SVRCONN
|
|
131
|
+
... username=${NONE}
|
|
132
|
+
... password=${NONE}
|
|
133
|
+
|
|
134
|
+
Disconnect All MQ Connections
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This connects to a **real queue manager** using a **real MQ channel**.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## MQ administration notes
|
|
142
|
+
|
|
143
|
+
If required:
|
|
144
|
+
|
|
145
|
+
- Ensure the MQ channel exists and is running
|
|
146
|
+
- Ensure the MQ listener is active
|
|
147
|
+
- Common admin channel: `SYSTEM.ADMIN.SVRCONN`
|
|
148
|
+
|
|
149
|
+
Use **IBM MQ Explorer** or platform tooling to manage this.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Troubleshooting
|
|
154
|
+
|
|
155
|
+
Errors mentioning `ibmmqc`, DLL load failures, or missing shared libraries are **IBM MQ / ibmmq environment problems**.
|
|
156
|
+
|
|
157
|
+
Re-check:
|
|
158
|
+
- IBM MQ installation
|
|
159
|
+
- `ibmmq` prerequisites
|
|
160
|
+
- Environment variables
|
|
161
|
+
- 32-bit vs 64-bit mismatches
|
|
162
|
+
|
|
163
|
+
Official reference:
|
|
164
|
+
https://github.com/ibm-messaging/mq-mqi-python?tab=readme-ov-file#prerequisites
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Acknowledgements ❤️
|
|
169
|
+
|
|
170
|
+
**Special thanks to UWV** for sponsoring, supporting, and **open-sourcing** this library.
|
|
171
|
+
|
|
172
|
+
By funding real-world test tooling and releasing it as open source, UWV has contributed back to the broader Robot Framework and IBM MQ communities.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# robotframework-ibmmq
|
|
2
|
+
|
|
3
|
+
**Robot Framework keywords for IBM MQ — powered by the `ibmmq` Python package.**
|
|
4
|
+
|
|
5
|
+
This library is a **thin, explicit Robot Framework wrapper around the `ibmmq` Python package**.
|
|
6
|
+
It does **not** abstract away IBM MQ complexity — and that is **by design**.
|
|
7
|
+
|
|
8
|
+
If you use this library, you are using **real IBM MQ**, with **real native dependencies**, exactly like production.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What this library is (and is not)
|
|
13
|
+
|
|
14
|
+
✅ **IS**
|
|
15
|
+
- A Robot Framework library for **connecting to IBM MQ**
|
|
16
|
+
- A wrapper around the **`ibmmq` Python package**
|
|
17
|
+
- Designed for **real integration testing**, not mocks or file-based substitutes
|
|
18
|
+
|
|
19
|
+
❌ **IS NOT**
|
|
20
|
+
- A pure-Python library
|
|
21
|
+
- A “just pip install and go” solution
|
|
22
|
+
- A fake or simulated MQ implementation
|
|
23
|
+
|
|
24
|
+
If you are looking for something that avoids native dependencies, **this library is not for you**.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🚨 Read this first: IBM MQ prerequisites are mandatory
|
|
29
|
+
|
|
30
|
+
This library **directly depends on `ibmmq`**, which is a **Python extension module backed by IBM MQ native libraries**.
|
|
31
|
+
|
|
32
|
+
> **If `ibmmq` does not work on your machine, this Robot Framework library will not work either.**
|
|
33
|
+
|
|
34
|
+
Before you do *anything else*, you **must** read and follow the official `ibmmq` prerequisites:
|
|
35
|
+
|
|
36
|
+
👉 **Official ibmmq prerequisites (REQUIRED):**
|
|
37
|
+
https://github.com/ibm-messaging/mq-mqi-python?tab=readme-ov-file#prerequisites
|
|
38
|
+
|
|
39
|
+
### In practical terms, this means:
|
|
40
|
+
|
|
41
|
+
You **must have**, on the machine where tests run:
|
|
42
|
+
|
|
43
|
+
- ✅ IBM MQ C Client
|
|
44
|
+
- ✅ IBM MQ SDK
|
|
45
|
+
- ✅ A working C/C++ build toolchain
|
|
46
|
+
- ✅ Correct environment variables (e.g. `MQ_FILE_PATH` if MQ is not installed in the default location)
|
|
47
|
+
|
|
48
|
+
If any of the above is missing, you will see errors such as:
|
|
49
|
+
|
|
50
|
+
- `ModuleNotFoundError: No module named 'ibmmqc'`
|
|
51
|
+
- DLL load failures
|
|
52
|
+
- Import errors during `pip install` or at runtime
|
|
53
|
+
|
|
54
|
+
These are **environment issues**, not bugs in this library.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Platform notes
|
|
59
|
+
|
|
60
|
+
- ✅ Windows: Supported
|
|
61
|
+
- ✅ Linux: Supported
|
|
62
|
+
- ❌ No native MQ installation = no support
|
|
63
|
+
|
|
64
|
+
On Windows, a proper **Visual C++ build environment is required**, because `ibmmq` includes native extensions.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
### Step 1 — Verify `ibmmq` works first (strongly recommended)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python -c "import ibmmq; print('ibmmq OK')"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If this fails, **stop here** and fix your IBM MQ / `ibmmq` installation first.
|
|
77
|
+
|
|
78
|
+
### Step 2 — Install this library
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install robotframework-ibmmq
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
No magic. No hidden installers.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Usage example
|
|
89
|
+
|
|
90
|
+
```robot
|
|
91
|
+
*** Settings ***
|
|
92
|
+
Library MQLibrary
|
|
93
|
+
|
|
94
|
+
*** Test Cases ***
|
|
95
|
+
Connect to IBM MQ
|
|
96
|
+
Connect MQ
|
|
97
|
+
... queue_manager=QM_EXAMPLE
|
|
98
|
+
... hostname=mq.example.internal
|
|
99
|
+
... port=1414
|
|
100
|
+
... channel=SYSTEM.ADMIN.SVRCONN
|
|
101
|
+
... username=${NONE}
|
|
102
|
+
... password=${NONE}
|
|
103
|
+
|
|
104
|
+
Disconnect All MQ Connections
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This connects to a **real queue manager** using a **real MQ channel**.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## MQ administration notes
|
|
112
|
+
|
|
113
|
+
If required:
|
|
114
|
+
|
|
115
|
+
- Ensure the MQ channel exists and is running
|
|
116
|
+
- Ensure the MQ listener is active
|
|
117
|
+
- Common admin channel: `SYSTEM.ADMIN.SVRCONN`
|
|
118
|
+
|
|
119
|
+
Use **IBM MQ Explorer** or platform tooling to manage this.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Troubleshooting
|
|
124
|
+
|
|
125
|
+
Errors mentioning `ibmmqc`, DLL load failures, or missing shared libraries are **IBM MQ / ibmmq environment problems**.
|
|
126
|
+
|
|
127
|
+
Re-check:
|
|
128
|
+
- IBM MQ installation
|
|
129
|
+
- `ibmmq` prerequisites
|
|
130
|
+
- Environment variables
|
|
131
|
+
- 32-bit vs 64-bit mismatches
|
|
132
|
+
|
|
133
|
+
Official reference:
|
|
134
|
+
https://github.com/ibm-messaging/mq-mqi-python?tab=readme-ov-file#prerequisites
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Acknowledgements ❤️
|
|
139
|
+
|
|
140
|
+
**Special thanks to UWV** for sponsoring, supporting, and **open-sourcing** this library.
|
|
141
|
+
|
|
142
|
+
By funding real-world test tooling and releasing it as open source, UWV has contributed back to the broader Robot Framework and IBM MQ communities.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.10.7,<0.11.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "robotframework-ibmmq"
|
|
8
|
+
version = "0.1.2"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name="Niels Janssen", email="niels.janssen@uwv.nl" },
|
|
11
|
+
]
|
|
12
|
+
maintainers = [
|
|
13
|
+
{ name="Niels Janssen", email="niels.janssen@uwv.nl" },
|
|
14
|
+
]
|
|
15
|
+
description = "A Robotframework library for IBM MQ"
|
|
16
|
+
keywords = ["robotframework", "ibm", "mqlibrary"]
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
license = "EUPL-1.2"
|
|
19
|
+
requires-python = ">=3.9.0"
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Topic :: Software Development :: Testing",
|
|
24
|
+
"Framework :: Robot Framework",
|
|
25
|
+
"Framework :: Robot Framework :: Library",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"robotframework (>=7.3.0,<8.3.0)",
|
|
31
|
+
"ibmmq (>=2.0.0,<3.0.0)"
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
dev = [
|
|
36
|
+
"pyyaml>=6.0.3",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
deploy = ["twine", "build", "wheel"]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/nluwv/robotframework-ibmmq"
|
|
45
|
+
Documentation = "https://robotframework-ibmmq.netlify.app/IBMMQLibrary.html"
|
|
46
|
+
Repository = "https://github.com/nluwv/robotframework-ibmmq.git"
|
|
47
|
+
Issues = "https://github.com/nluwv/robotframework-ibmmq/issues"
|
|
48
|
+
|
|
49
|
+
[tool.uv.build-backend]
|
|
50
|
+
module-name = "IBMMQLibrary"
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
import ibmmq
|
|
5
|
+
from robot.api import logger
|
|
6
|
+
from robot.api.deco import keyword, library
|
|
7
|
+
from robot.utils import timestr_to_secs
|
|
8
|
+
|
|
9
|
+
from .exceptions import MQMIError_handling
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _new_md() -> ibmmq.MD:
|
|
13
|
+
"""Return a fresh MQ Message Descriptor (MD)."""
|
|
14
|
+
md = ibmmq.MD()
|
|
15
|
+
md.Version = ibmmq.CMQC.MQMD_VERSION_2
|
|
16
|
+
return md
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@library
|
|
20
|
+
class IBMMQLibrary:
|
|
21
|
+
"""
|
|
22
|
+
IBMMQLibrary - A Robot Framework Library for IBM MQ Integration with multi-alias connection support.
|
|
23
|
+
|
|
24
|
+
This library enables easy interaction with IBM MQ message queues from Robot Framework.
|
|
25
|
+
|
|
26
|
+
It supports multiple simultaneous connections to different queue managers through aliases.
|
|
27
|
+
If only one connection is used, the alias parameter can be omitted (defaults to "default").
|
|
28
|
+
|
|
29
|
+
MQ server and MQ client must use compatible code pages.
|
|
30
|
+
If code pages are not compatible error `2539: MQRC_CHANNEL_CONFIG_ERROR` will occur.
|
|
31
|
+
If the client uses codepage 65001 (the windows equivalent of UTF-8) and the server uses codepage 850
|
|
32
|
+
the channel name will not be found and the connection will fail.
|
|
33
|
+
Forcing the client code page can be done with the `chcp` command.
|
|
34
|
+
For example `chcp 437` to use default ascii code page.
|
|
35
|
+
|
|
36
|
+
=== Examples ===
|
|
37
|
+
| `Connect MQ` queue_manager=QM1
|
|
38
|
+
| ... hostname=localhost
|
|
39
|
+
| ... port=1414
|
|
40
|
+
| ... channel=DEV.APP.SVRCONN
|
|
41
|
+
| ... username=%{USERNAME}
|
|
42
|
+
| ... password=%{PASSWORD}
|
|
43
|
+
| `Put MQ Message` queue=QUEUE.TEST message=Hello
|
|
44
|
+
| ${messages} `Get MQ Message` queue=QUEUE.TEST max_messages=1
|
|
45
|
+
| `Clear MQ Queue` queue=QUEUE.TEST
|
|
46
|
+
| `Disconnect MQ`
|
|
47
|
+
"""
|
|
48
|
+
ROBOT_LIBRARY_SCOPE = 'GLOBAL'
|
|
49
|
+
|
|
50
|
+
def __init__(self):
|
|
51
|
+
self.connections = {} # Holds aliases mapped to queue manager connections
|
|
52
|
+
|
|
53
|
+
# Check for unsupported code page 65001 (Windows utf-8)
|
|
54
|
+
if os.name == 'nt':
|
|
55
|
+
codepage = int(subprocess.getoutput("chcp").split()[-1])
|
|
56
|
+
if codepage == 65001:
|
|
57
|
+
logger.warn("This shell uses codepage 65001, which is not supported by MQ, you will likely encounter error: 2539: MQRC_CHANNEL_CONFIG_ERROR.\n Use `chcp 437` in your shell to switch to a compatible codepage.")
|
|
58
|
+
|
|
59
|
+
@keyword(name="Connect MQ", tags=["Connection"])
|
|
60
|
+
def connect_mq(self, queue_manager: str, hostname: str, port: int, channel: str, username: str | None = None, password: str | None = None, alias: str = "default"):
|
|
61
|
+
"""
|
|
62
|
+
Connects to the remote queue manager and stores it under an alias.
|
|
63
|
+
|
|
64
|
+
| =Argument= | =Description= |
|
|
65
|
+
| ``queue_manager`` | Name of the queue manager |
|
|
66
|
+
| ``hostname`` | Hostname of the MQ server |
|
|
67
|
+
| ``port`` | Port used to connect |
|
|
68
|
+
| ``channel`` | Channel name used for communication |
|
|
69
|
+
| ``username`` | Username for authentication (optional) |
|
|
70
|
+
| ``password`` | Password for authentication (optional) |
|
|
71
|
+
| ``alias`` | Connection alias (default = "default") |
|
|
72
|
+
|
|
73
|
+
=== Example ===
|
|
74
|
+
| `Connect MQ` queue_manager=QM1
|
|
75
|
+
| ... hostname=localhost
|
|
76
|
+
| ... port=1414
|
|
77
|
+
| ... channel=DEV.APP.SVRCONN
|
|
78
|
+
| ... username=&{USERNAME}
|
|
79
|
+
| ... password=&{PASSWORD}
|
|
80
|
+
"""
|
|
81
|
+
if alias in self.connections:
|
|
82
|
+
raise ValueError(f"Alias '{alias}' is already connected.")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
conn_info = f"{hostname}({port})"
|
|
86
|
+
cd = ibmmq.CD()
|
|
87
|
+
cd.ChannelName = channel
|
|
88
|
+
cd.ConnectionName = conn_info
|
|
89
|
+
cd.ChannelType = ibmmq.CMQXC.MQCHT_CLNTCONN
|
|
90
|
+
cd.TransportType = ibmmq.CMQXC.MQXPT_TCP
|
|
91
|
+
|
|
92
|
+
qmgr = ibmmq.QueueManager(None)
|
|
93
|
+
qmgr.connect_tcp_client(
|
|
94
|
+
name=queue_manager,
|
|
95
|
+
cd=cd,
|
|
96
|
+
channel=channel,
|
|
97
|
+
conn_name=conn_info,
|
|
98
|
+
user=username,
|
|
99
|
+
password=password
|
|
100
|
+
)
|
|
101
|
+
self.connections[alias] = qmgr
|
|
102
|
+
logger.info(f"Connected to MQ with alias '{alias}'.")
|
|
103
|
+
|
|
104
|
+
except ibmmq.MQMIError as e:
|
|
105
|
+
MQMIError_handling(e, mqmanager=queue_manager, mqhost=hostname, mqport=port, mqchannel=channel)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.error(f"Failed to connect to MQ: {e}")
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _get_qmgr(self, alias: str):
|
|
112
|
+
if alias not in self.connections:
|
|
113
|
+
raise ValueError("No valid MQ alias provided or no default connection available.")
|
|
114
|
+
return self.connections[alias]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@keyword(name="Put MQ Message", tags=["Put"])
|
|
118
|
+
def put_message(self, queue: str, message: str, ccsid: int = 1208, alias: str = "default"):
|
|
119
|
+
"""
|
|
120
|
+
Puts a message onto the target queue.
|
|
121
|
+
|
|
122
|
+
| =Argument= | =Description= |
|
|
123
|
+
| ``queue`` | Name of the target queue |
|
|
124
|
+
| ``message`` | Message content to put on the queue |
|
|
125
|
+
| ``ccsid`` | Character encoding set (default 1208) |
|
|
126
|
+
| ``alias`` | Alias of the MQ connection |
|
|
127
|
+
|
|
128
|
+
=== Example ===
|
|
129
|
+
| `Put MQ Message` queue=QUEUE.TEST message=Hello World
|
|
130
|
+
"""
|
|
131
|
+
qmgr = self._get_qmgr(alias)
|
|
132
|
+
queue_obj = None
|
|
133
|
+
try:
|
|
134
|
+
queue_obj = ibmmq.Queue(qmgr, queue)
|
|
135
|
+
md = _new_md()
|
|
136
|
+
md.CodedCharSetId = ccsid
|
|
137
|
+
message_bytes = message.encode('utf-8')
|
|
138
|
+
queue_obj.put(message_bytes, md)
|
|
139
|
+
logger.info(f"Message put on queue '{queue}' via alias '{alias}'.")
|
|
140
|
+
finally:
|
|
141
|
+
if queue_obj:
|
|
142
|
+
queue_obj.close()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@keyword(name="Get MQ Messages", tags=["Retrieve"])
|
|
146
|
+
def get_messages(self, queue: str, message_amount: int = 1, convert: bool = True, timeout: str = 0, alias: str = "default") -> list:
|
|
147
|
+
"""
|
|
148
|
+
Retrieves messages from a queue and returns it as a list.
|
|
149
|
+
Removes the messages from the queue on retrieval.
|
|
150
|
+
|
|
151
|
+
| =Argument= | =Description= |
|
|
152
|
+
| ``queue`` | Queue to read messages from |
|
|
153
|
+
| ``message_amount`` | Number of messages to retrieve, fails if it doesn't |
|
|
154
|
+
| ``convert`` | Convert message (MQGMO_CONVERT) if True |
|
|
155
|
+
| ``timeout`` | Timeout per message in seconds (default = 0) |
|
|
156
|
+
| ``alias`` | Alias of the MQ connection (default = "default") |
|
|
157
|
+
|
|
158
|
+
=== Example ===
|
|
159
|
+
| ${msgs} `Get MQ Messages` queue=QUEUE.TEST message_amount=5 timeout=2
|
|
160
|
+
"""
|
|
161
|
+
qmgr = self._get_qmgr(alias)
|
|
162
|
+
queue_obj = None
|
|
163
|
+
messages = []
|
|
164
|
+
try:
|
|
165
|
+
gmo = ibmmq.GMO()
|
|
166
|
+
gmo.Options = ibmmq.CMQC.MQGMO_WAIT | (ibmmq.CMQC.MQGMO_CONVERT if convert else 0)
|
|
167
|
+
|
|
168
|
+
timeout_ms = int(timestr_to_secs(timeout) * 1000)
|
|
169
|
+
gmo.WaitInterval = timeout_ms
|
|
170
|
+
|
|
171
|
+
queue_obj = ibmmq.Queue(qmgr, queue)
|
|
172
|
+
for _ in range(message_amount):
|
|
173
|
+
md = _new_md()
|
|
174
|
+
try:
|
|
175
|
+
message = queue_obj.get(None, md, gmo)
|
|
176
|
+
try:
|
|
177
|
+
decoded = message.decode("utf-8")
|
|
178
|
+
except UnicodeDecodeError:
|
|
179
|
+
logger.warn("UTF-8 decoding failed, falling back to ISO-8859-1.")
|
|
180
|
+
decoded = message.decode("iso-8859-1")
|
|
181
|
+
messages.append(decoded)
|
|
182
|
+
logger.info(f"Received message from queue '{queue}': {decoded}")
|
|
183
|
+
except ibmmq.MQMIError as e:
|
|
184
|
+
if e.reason == ibmmq.CMQC.MQRC_NO_MSG_AVAILABLE:
|
|
185
|
+
logger.info(f"No more messages available on queue '{queue}'.")
|
|
186
|
+
break
|
|
187
|
+
logger.error(f"Failed to get message from queue '{queue}': {e}")
|
|
188
|
+
raise
|
|
189
|
+
if len(messages) == message_amount:
|
|
190
|
+
return messages
|
|
191
|
+
message = f"Expected {message_amount} message(s), but received {len(messages)}."
|
|
192
|
+
logger.error(message)
|
|
193
|
+
raise AssertionError(message)
|
|
194
|
+
finally:
|
|
195
|
+
if queue_obj:
|
|
196
|
+
queue_obj.close()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@keyword(name="Browse MQ Messages", tags=["Retrieve"])
|
|
200
|
+
def browse_messages(self, queue: str, max_messages: int = 1, timeout: str = '5s', convert: bool = True, alias: str = "default") -> list:
|
|
201
|
+
"""
|
|
202
|
+
Browses for messages on ``queue`` and returns them in a list.
|
|
203
|
+
Doesn't delete a message after being browsed.
|
|
204
|
+
|
|
205
|
+
| =Argument= | =Description= |
|
|
206
|
+
| ``queue`` | Queue to listen on |
|
|
207
|
+
| ``max_messages`` | Maximum number of messages it tries to retrieve |
|
|
208
|
+
| ``timeout`` | Timeout per message in milliseconds (default 5000) |
|
|
209
|
+
| ``convert`` | Convert message encoding if True |
|
|
210
|
+
| ``alias`` | MQ connection alias (default = "default") |
|
|
211
|
+
|
|
212
|
+
=== Example ===
|
|
213
|
+
| ${msgs} `Browse MQ Messages` queue=QUEUE.TEST max_messages=3
|
|
214
|
+
"""
|
|
215
|
+
qmgr = self._get_qmgr(alias)
|
|
216
|
+
messages = []
|
|
217
|
+
queue_obj = None
|
|
218
|
+
try:
|
|
219
|
+
timeout_ms = int(timestr_to_secs(timeout) * 1000)
|
|
220
|
+
queue_obj = ibmmq.Queue(qmgr, queue, ibmmq.CMQC.MQOO_BROWSE)
|
|
221
|
+
logger.info(f"Browsing messages on '{queue}' via alias '{alias}'...")
|
|
222
|
+
|
|
223
|
+
for i in range(max_messages):
|
|
224
|
+
gmo = ibmmq.GMO()
|
|
225
|
+
gmo.Options = ibmmq.CMQC.MQGMO_WAIT | (ibmmq.CMQC.MQGMO_CONVERT if convert else 0)
|
|
226
|
+
gmo.WaitInterval = timeout_ms
|
|
227
|
+
gmo.Options |= ibmmq.CMQC.MQGMO_BROWSE_FIRST if i == 0 else ibmmq.CMQC.MQGMO_BROWSE_NEXT
|
|
228
|
+
|
|
229
|
+
md = _new_md()
|
|
230
|
+
try:
|
|
231
|
+
message = queue_obj.get(None, md, gmo)
|
|
232
|
+
try:
|
|
233
|
+
decoded = message.decode("utf-8")
|
|
234
|
+
except UnicodeDecodeError:
|
|
235
|
+
logger.warn("UTF-8 decoding failed, falling back to ISO-8859-1.")
|
|
236
|
+
decoded = message.decode("iso-8859-1")
|
|
237
|
+
messages.append(decoded)
|
|
238
|
+
logger.info(f"Browsed message {i + 1}: {decoded}")
|
|
239
|
+
except ibmmq.MQMIError as e:
|
|
240
|
+
if e.reason == ibmmq.CMQC.MQRC_NO_MSG_AVAILABLE:
|
|
241
|
+
logger.info(f"No more messages available to browse on queue '{queue}'.")
|
|
242
|
+
break
|
|
243
|
+
logger.error(f"Error while browsing message: {e}")
|
|
244
|
+
raise
|
|
245
|
+
return messages
|
|
246
|
+
finally:
|
|
247
|
+
if queue_obj:
|
|
248
|
+
queue_obj.close()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@keyword(name="Clear MQ Queue", tags=["Cleanup"])
|
|
252
|
+
def clear_queue(self, queue: str, alias: str = "default"):
|
|
253
|
+
"""
|
|
254
|
+
Removes all messages from a queue immediately without waiting.
|
|
255
|
+
|
|
256
|
+
| =Argument= | =Description= |
|
|
257
|
+
| ``queue`` | The queue to clear |
|
|
258
|
+
| ``alias`` | MQ connection alias (default = "default") |
|
|
259
|
+
|
|
260
|
+
=== Example ===
|
|
261
|
+
| `Clear MQ Queue` queue=QUEUE.TEST
|
|
262
|
+
"""
|
|
263
|
+
qmgr = self._get_qmgr(alias)
|
|
264
|
+
queue_obj = None
|
|
265
|
+
try:
|
|
266
|
+
gmo = ibmmq.GMO()
|
|
267
|
+
gmo.Options = ibmmq.CMQC.MQGMO_NO_WAIT
|
|
268
|
+
gmo.WaitInterval = 0
|
|
269
|
+
queue_obj = ibmmq.Queue(qmgr, queue, ibmmq.CMQC.MQOO_INPUT_AS_Q_DEF)
|
|
270
|
+
|
|
271
|
+
logger.info(f"Clearing all messages from queue '{queue}' via alias '{alias}'...")
|
|
272
|
+
|
|
273
|
+
cleared_count = 0
|
|
274
|
+
while True:
|
|
275
|
+
md = _new_md()
|
|
276
|
+
try:
|
|
277
|
+
_ = queue_obj.get(None, md, gmo)
|
|
278
|
+
cleared_count += 1
|
|
279
|
+
except ibmmq.MQMIError as e:
|
|
280
|
+
if e.reason == ibmmq.CMQC.MQRC_NO_MSG_AVAILABLE:
|
|
281
|
+
break
|
|
282
|
+
logger.error(f"Error while clearing queue: {e}")
|
|
283
|
+
raise
|
|
284
|
+
|
|
285
|
+
logger.info(f"Cleared {cleared_count} message(s) from queue '{queue}' via alias '{alias}'.")
|
|
286
|
+
|
|
287
|
+
# Final verification that queue is empty
|
|
288
|
+
md = _new_md()
|
|
289
|
+
try:
|
|
290
|
+
queue_obj.get(None, md, gmo)
|
|
291
|
+
raise AssertionError(f"Queue '{queue}' still contains messages after clearing.")
|
|
292
|
+
except ibmmq.MQMIError as e:
|
|
293
|
+
if e.reason == ibmmq.CMQC.MQRC_NO_MSG_AVAILABLE:
|
|
294
|
+
logger.info(f"Verified queue '{queue}' is empty.")
|
|
295
|
+
else:
|
|
296
|
+
logger.error(f"Unexpected error while verifying queue: {e}")
|
|
297
|
+
raise
|
|
298
|
+
finally:
|
|
299
|
+
if queue_obj:
|
|
300
|
+
queue_obj.close()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@keyword(name="Disconnect MQ", tags=["Connection"])
|
|
304
|
+
def disconnect_mq(self, alias: str = "default"):
|
|
305
|
+
"""Disconnects a specific MQ connection using alias."""
|
|
306
|
+
if alias in self.connections:
|
|
307
|
+
try:
|
|
308
|
+
self.connections[alias].disconnect()
|
|
309
|
+
logger.info(f"Disconnected from MQ alias '{alias}'.")
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.warn(f"Error while disconnecting alias '{alias}': {e}")
|
|
312
|
+
finally:
|
|
313
|
+
del self.connections[alias]
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@keyword(name="Disconnect All MQ Connections", tags=["Connection"])
|
|
317
|
+
def disconnect_all(self):
|
|
318
|
+
"""Disconnects all active MQ connections."""
|
|
319
|
+
for alias in list(self.connections.keys()):
|
|
320
|
+
self.disconnect_mq(alias)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import ibmmq
|
|
2
|
+
from robot.api import logger
|
|
3
|
+
|
|
4
|
+
def MQMIError_handling(mqerror, **manager_settings):
|
|
5
|
+
match mqerror.reason:
|
|
6
|
+
case ibmmq.CMQC.MQRC_Q_MGR_NAME_ERROR: # 2058
|
|
7
|
+
message = f"Queue Manager {manager_settings['mqmanager']} does not exist or is unavailable."
|
|
8
|
+
logger.error(message)
|
|
9
|
+
raise ValueError(mqerror)
|
|
10
|
+
case ibmmq.CMQC.MQRC_HOST_NOT_AVAILABLE: # 2538
|
|
11
|
+
message = f"Cannot connect to MQ host {manager_settings['mqhost']}:{manager_settings['mqport']}. Host not available or refusing connection."
|
|
12
|
+
logger.error(message)
|
|
13
|
+
raise ValueError(mqerror)
|
|
14
|
+
case ibmmq.CMQC.MQRC_UNKNOWN_CHANNEL_NAME: # 2540
|
|
15
|
+
message = f"Channel {manager_settings['mqchannel']} is unknown or not configured correctly on the MQ server."
|
|
16
|
+
logger.error(message)
|
|
17
|
+
raise ValueError(mqerror)
|
|
18
|
+
case ibmmq.CMQC.MQRC_SECURITY_ERROR: # 2063
|
|
19
|
+
message = f"Authentication failed. Check username and password for queue manager {manager_settings['mqmanager']}."
|
|
20
|
+
logger.error(message)
|
|
21
|
+
raise ValueError(mqerror)
|
|
22
|
+
case _:
|
|
23
|
+
logger.error(f"MQ connection failed with reason code {mqerror.reason}: {mqerror}") # Generiek maken
|
|
24
|
+
raise
|