Skip to content

Commit 7bda8ec

Browse files
committed
18076 FIX SMS (using modem API): Use new API endpoint if needed
SUP-24364 Change-Id: I6dad67042e3459e55421bb9cd46a9d70f9b809bd
1 parent dc25b89 commit 7bda8ec

2 files changed

Lines changed: 111 additions & 23 deletions

File tree

.werks/18076

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Title: SMS (using modem API): Use new API endpoint for TRB140 if needed
2+
Class: fix
3+
Compatible: incomp
4+
Component: notifications
5+
Date: 1752490749
6+
Edition: cre
7+
Level: 1
8+
Version: 2.2.0p45
9+
10+
Since firmware Version 7.14 the notification script produced an error on
11+
sending notifications.
12+
This was caused by a change in the API specs from this firmware version on.
13+
14+
The script now tries to send the SMS in the previous way. If it fails, the new
15+
API endpoint with token authentication is used.
16+
This means that also firmware versions below 7.14 are still supported by the
17+
notitication script.
18+
19+
If you use firmware version 7.14 or higher, the user for sending notifications
20+
needs at least a group with permissions:
21+
22+
LI: "Write action": "Allow"
23+
LI: "Write Access": "Services > Mobile Utilities > Messages"
24+
LI: "Read action": "Allow"
25+
LI: "Read access": "Services > Mobile Utilities > Messages".

cmk/notification_plugins/sms_api.py

Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sys
77
from collections.abc import MutableMapping
88
from dataclasses import dataclass
9-
from typing import NoReturn
9+
from typing import Callable, NoReturn, Self
1010

1111
import requests
1212

@@ -54,10 +54,9 @@ class RequestParameter:
5454

5555
@dataclass
5656
class Context:
57-
url: str
5857
request_parameter: RequestParameter
5958
message: Message
60-
data: dict[str, str]
59+
send_function: Callable[[Self], int]
6160

6261

6362
# .
@@ -110,15 +109,9 @@ def _get_context_parameter(raw_context: PluginNotificationContext) -> Errors | C
110109
endpoint = raw_context["PARAMETER_MODEM_TYPE"]
111110
if endpoint == "trb140":
112111
return Context(
113-
url=request_parameter.url + "/cgi-bin/sms_send",
114112
request_parameter=request_parameter,
113+
send_function=_send_func_trb140,
115114
message=message,
116-
data={
117-
"username": request_parameter.user,
118-
"password": request_parameter.pwd,
119-
"number": request_parameter.recipient,
120-
"text": message,
121-
},
122115
)
123116

124117
return Errors(["Unknown unsupported modem: %s" % endpoint])
@@ -157,25 +150,95 @@ def _get_request_params_from_context(
157150
# '----------------------------------------------------------------------'
158151

159152

160-
def process_notifications(context: Context) -> int:
161-
"""Main processing of notifications for this API endpoint."""
162-
response = requests.post(
163-
context.url,
164-
proxies=context.request_parameter.proxies,
165-
timeout=context.request_parameter.timeout,
166-
data=context.data,
167-
verify=context.request_parameter.verify,
168-
)
153+
def _send_func_trb140(context: Context) -> int:
154+
"""Main processing of notifications for trb140"""
155+
try:
156+
response = _trb140_mobile_post(context)
157+
if "<!doctype html" in response.text.lower():
158+
return _trb140_api(context)
159+
160+
response.raise_for_status()
169161

170-
if response.status_code != 200 or response.content != b"OK\n":
171-
sys.stderr.write(f"Error Status: {response.status_code} Details: {response.content!r}\n")
162+
if response.status_code != 200 or not response.content.startswith(b"OK\n"):
163+
sys.stderr.write(
164+
f"Error Status: {response.status_code} Details: {response.content!r}\n"
165+
)
166+
return 2
167+
168+
except requests.exceptions.HTTPError as e:
169+
sys.stderr.write(f"HTTPError sending SMS: {e}, {response.content!r}\n")
172170
return 2
173171

174-
sys.stdout.write("Notification successfully send via sms.\n")
172+
except Exception as e:
173+
sys.stderr.write(f"Error sending SMS: {e}\n")
174+
return 2
175175

176+
sys.stdout.write("Notification successfully sent via sms.\n")
176177
return 0
177178

178179

180+
def _trb140_api(context: Context) -> int:
181+
"""Since firmware 7.14 the API has to be used"""
182+
try:
183+
token_response = requests.post(
184+
context.request_parameter.url + "/api/login",
185+
json={
186+
"username": context.request_parameter.user,
187+
"password": context.request_parameter.pwd,
188+
},
189+
headers={"Content-Type": "application/json"},
190+
proxies=context.request_parameter.proxies,
191+
timeout=context.request_parameter.timeout,
192+
verify=context.request_parameter.verify,
193+
)
194+
token_response.raise_for_status()
195+
token = token_response.json().get("data", {}).get("token")
196+
if not token:
197+
raise ValueError("Got no session token.\n")
198+
199+
sms_data = {
200+
"number": context.request_parameter.recipient,
201+
"message": context.message,
202+
"modem": "3-1",
203+
}
204+
205+
sms_response = requests.post(
206+
context.request_parameter.url + "/api/messages/actions/send",
207+
json={"data": sms_data},
208+
headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}"},
209+
proxies=context.request_parameter.proxies,
210+
timeout=context.request_parameter.timeout,
211+
verify=context.request_parameter.verify,
212+
)
213+
sms_response.raise_for_status()
214+
215+
if sms_response.json().get("success") is True and sms_response.status_code == 200:
216+
sys.stdout.write("Notification successfully sent via sms.\n")
217+
return 0
218+
219+
return 2
220+
221+
except ValueError as e:
222+
sys.stderr.write(f"Error calling API: {e}")
223+
return 2
224+
225+
226+
def _trb140_mobile_post(context: Context) -> requests.Response:
227+
"""This endpoint has to be used until firmware version 7.13"""
228+
return requests.post(
229+
context.request_parameter.url + "/cgi-bin/sms_send",
230+
proxies=context.request_parameter.proxies,
231+
timeout=context.request_parameter.timeout,
232+
data={
233+
"username": context.request_parameter.user,
234+
"password": context.request_parameter.pwd,
235+
"number": context.request_parameter.recipient,
236+
"text": context.message,
237+
},
238+
verify=context.request_parameter.verify,
239+
)
240+
241+
179242
def main() -> NoReturn:
180243
"""Construct needed context and call the related class."""
181244
raw_context: PluginNotificationContext = collect_context()
@@ -186,7 +249,7 @@ def main() -> NoReturn:
186249
sys.stdout.write(" ".join(context))
187250
sys.exit(2)
188251

189-
sys.exit(process_notifications(context))
252+
sys.exit(context.send_function(context))
190253

191254

192255
# .

0 commit comments

Comments
 (0)