jetstream-api 0.6.6__tar.gz → 0.6.7__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 (25) hide show
  1. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/PKG-INFO +1 -1
  2. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/__init__.py +2 -1
  3. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/admin.py +141 -31
  4. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/common.py +12 -0
  5. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/msg.py +2668 -2668
  6. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/properties.py +369 -215
  7. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/qmgr.py +5 -1
  8. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/util.py +28 -2
  9. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/jetstream_api.egg-info/PKG-INFO +1 -1
  10. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/pyproject.toml +1 -1
  11. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/LICENSE.txt +0 -0
  12. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/README.md +0 -0
  13. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/admintopic.py +0 -0
  14. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/connectivity.py +0 -0
  15. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/exception.py +0 -0
  16. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/pattern.py +0 -0
  17. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/producer.py +0 -0
  18. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/py.typed +0 -0
  19. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/ilink/topic.py +0 -0
  20. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/jetstream_api.egg-info/SOURCES.txt +0 -0
  21. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/jetstream_api.egg-info/dependency_links.txt +0 -0
  22. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/jetstream_api.egg-info/requires.txt +0 -0
  23. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/jetstream_api.egg-info/top_level.txt +0 -0
  24. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/setup.cfg +0 -0
  25. {jetstream_api-0.6.6 → jetstream_api-0.6.7}/tests/test_example.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jetstream-api
3
- Version: 0.6.6
3
+ Version: 0.6.7
4
4
  Summary: Python client API for iLink M.O.M. (Message Oriented Middleware)
5
5
  Author: snowjeans
6
6
  License: Boost Software License - Version 1.0 - August 17th, 2003
@@ -1,6 +1,6 @@
1
1
  """iLink - Python client API for iLink M.O.M. (Message Oriented Middleware)."""
2
2
 
3
- __version__ = "0.6.6"
3
+ __version__ = "0.6.7"
4
4
 
5
5
  # --- Admin / Service ---
6
6
  from .admin import ILAdminService, ILAdminQmgr, ILLogBrowser, ILQueueBrowser
@@ -36,6 +36,7 @@ from .properties import (
36
36
  ILSecureFileProperty,
37
37
  ILQueueRealTimeInfo,
38
38
  ILQueueFlow,
39
+ ILQmgrDiag,
39
40
  )
40
41
 
41
42
  # --- Exceptions ---
@@ -7,6 +7,7 @@ import uuid
7
7
  from .common import ILC, ILRC, MatchOption
8
8
  from .connectivity import ILRequestHelper
9
9
  from .exception import (
10
+ UnsupportedOperationException,
10
11
  ILException,
11
12
  ILMsgTypeException,
12
13
  ILNoMsgException,
@@ -16,6 +17,7 @@ from .exception import (
16
17
  )
17
18
  from .msg import ILDataMsg, ILMsg, ILMsgUtil, ILReportMsg, JMSHeader
18
19
  from .properties import (
20
+ ILQmgrDiag,
19
21
  ILAccessRuleProperty,
20
22
  ILChannelMinorProperty,
21
23
  ILChannelProperty,
@@ -53,6 +55,9 @@ _ADMIN_QMGR_TOKEN = object()
53
55
 
54
56
  # Exception types that should propagate without re-wrapping
55
57
  _PASSTHROUGH_EXCEPTIONS = (
58
+ # 와이어 폭/길이 위반은 호출자가 고칠 수 있는 값 문제다 - 사유를
59
+ # 그대로 올려 준다. 감싸면 MIMQE_INTERNAL_ERROR 로만 보인다.
60
+ UnsupportedOperationException,
56
61
  ILException, ILOperationException, ILSessionException,
57
62
  ILMsgTypeException, ILNoMsgException, ILTimeoutException,
58
63
  )
@@ -458,6 +463,97 @@ class ILAdminService:
458
463
  except Exception as exc:
459
464
  raise ILException(exc) from exc
460
465
 
466
+ def _requireLastDiagSupport(self) -> None:
467
+ """진단 조회를 지원하지 않는 엔진이면 그 자리에서 실패시킨다.
468
+
469
+ Raises:
470
+ ILOperationException: 엔진이 rev 3380 미만일 때.
471
+
472
+ Note:
473
+ **구엔진에 보내면 예외가 아니라 정상 응답 모양으로 돌아온다.**
474
+ 요청 코드를 모르는 엔진은 ``MIMQE_INVALID_REQ_CODE`` 를 arg2 에 담아
475
+ 주므로, 게이트가 없으면 그 문자열을 "기동 실패 사유" 로 읽게 된다.
476
+ 조용한 오독이라 여기서 막는다.
477
+ """
478
+ if 0 < self.revision < ILC.LAST_DIAG_MIN_REVISION:
479
+ raise ILOperationException(
480
+ "MIMQE_NOT_SUPPORTED (last diag requires engine rev "
481
+ f"{ILC.LAST_DIAG_MIN_REVISION}+, current: {self.revision})"
482
+ )
483
+
484
+ def getLastDiag(self, qmgrName: str) -> ILQmgrDiag:
485
+ """큐 관리자의 **마지막 진단**을 읽는다 - 기동 실패 사유와 대체된 속성.
486
+
487
+ ``startQmgrWithStatus`` 가 ``"ERROR"`` 만 돌려주고 **왜** 인지는 알려주지
488
+ 않을 때 쓴다. 사유는 서버 로그에만 있었는데 이 조회로 응용이 받을 수 있다.
489
+
490
+ Args:
491
+ qmgrName (str): 조회할 큐 관리자 이름.
492
+
493
+ Returns:
494
+ ILQmgrDiag: 상태/사유/대체목록. ``state`` 는 ``"NONE"`` /
495
+ ``"STARTING"`` / ``"ERROR"`` 중 하나다.
496
+
497
+ Raises:
498
+ ILOperationException: 엔진이 rev 3380 미만이거나(지원 안 함),
499
+ **그 이름의 큐 관리자가 없을 때**(``MIMQE_OBJECT_NOT_FOUND``).
500
+ ILException: 통신 실패.
501
+
502
+ Warning:
503
+ **사유는 ``state`` 가 ``"ERROR"`` 일 때만 채워진다.** 비어 있다고
504
+ "실패한 적 없다" 로 읽으면 안 된다 - 엔진이 재시도할 때마다 사유를
505
+ 지우기 때문에, 실패한 큐 관리자도 대부분의 시점에는
506
+ ``STARTING`` + 빈 사유로 보인다.
507
+
508
+ 10.10.1.95 (7.0.1.3383) 실측 - 포트를 뺏어 기동을 막은 경우::
509
+
510
+ 0.0초 STARTING (빈값)
511
+ 11.1초 ERROR MIMQE_PORT_BINDING_ERROR (26385): Address already in use
512
+ 12.1초 STARTING (빈값)
513
+ 24.2초 ERROR (같은 사유)
514
+ 37.4초 ERROR (같은 사유)
515
+
516
+ 재시도 주기가 약 13초인데 ``ERROR`` 창은 1~2초다. 그래서 **한 번 물어
517
+ 보고 판단하면 안 되고**, 사유가 필요하면 1초 간격으로 한 주기 이상
518
+ 돌아야 한다. 아래 예제가 그 형태다.
519
+
520
+ Note:
521
+ **큐 관리자 전용이다.** 큐/채널/토픽/서비스/프록시/클러스터 이름으로
522
+ 부르면 ``MIMQE_OBJECT_NOT_FOUND`` 다.
523
+
524
+ 사유가 담기는 것은 **기동 실패**뿐이다. 속성 변경 거부처럼 즉시
525
+ 예외로 돌아오는 실패는 여기 남지 않는다 - 그건 예외를 보면 된다.
526
+
527
+ 서버가 큐 관리자 단위로 들고 있어서 **연결을 끊고 다시 붙어도 읽힌다.**
528
+ API 타임아웃으로 끊긴 뒤 재접속해 원인을 확인하는 용도로 쓸 수 있다.
529
+
530
+ Example::
531
+
532
+ import time
533
+
534
+ status = svc.startQmgrWithStatus("QMGR1", 30)
535
+ if status != "RUNNING":
536
+ # ERROR 창이 좁으므로 한 주기 동안 훑는다
537
+ reason = ""
538
+ for _ in range(15):
539
+ d = svc.getLastDiag("QMGR1")
540
+ if d.isError():
541
+ reason = d.getReason()
542
+ break
543
+ time.sleep(1)
544
+ print("기동 실패:", reason or "(사유 미확정)")
545
+ """
546
+ self._requireLastDiagSupport()
547
+ try:
548
+ res = self.service.request(ILC.MIMQ_GET_LAST_DIAG, qmgrName)
549
+ if res.getCode() != ILC.MIMQ_SUCCESSFUL_GET_LAST_DIAG:
550
+ raise ILOperationException(res.getArg2() or "MIMQE_OBJECT_NOT_FOUND")
551
+ return ILQmgrDiag(res.getArg1(), res.getArg2(), res.getArg3())
552
+ except _PASSTHROUGH_EXCEPTIONS:
553
+ raise
554
+ except Exception as exc:
555
+ raise ILException(exc) from exc
556
+
461
557
  def _topic_logger_supported(self) -> bool:
462
558
  """접속한 엔진이 큐관리자 속성의 **토픽 로거 구간**을 이해하는지 알려준다.
463
559
 
@@ -568,8 +664,8 @@ class ILAdminService:
568
664
  )
569
665
  try:
570
666
  ba = bytearray(ILC.MIMQ_LONG_REQUEST_ARG_LENGTH + len(homePath))
571
- ba_merge(qmgrName, ba, 0)
572
- ba_merge(homePath, ba, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
667
+ ba_merge(qmgrName, ba, 0, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
668
+ ba_merge(homePath, ba, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
573
669
  self.service.apply(
574
670
  ILC.MIMQ_SET_MANAGER_HOMEPATH,
575
671
  bytes(ba),
@@ -606,27 +702,30 @@ class ILAdminService:
606
702
 
607
703
  Warning:
608
704
  **값 검증은 엔진이 한다.** 이 API 는 문자열 길이(와이어 폭) 외의 값
609
- 범위를 검사하지 않으므로, 범위 밖 값을 넣었을 때 무슨 일이 일어나는지는
610
- **상대 엔진에 달려 있다.** 아래는 실측 결과다.
705
+ 범위를 검사하지 않는다. 그래서 **범위 밖 숫자를 넘겼을 때 무슨 일이
706
+ 일어나는지는 상대 엔진에 달려 있고, 결과가 셋으로 갈린다.** 셋 다
707
+ 현재 운영 중인 엔진에서 실제로 나온다::
708
+
709
+ vo = ILQmgrProperty("QMGR01", 19999)
710
+ vo.setSendTimeout(999999) # 범위 밖 (규격 1~864000)
711
+ svc.createQmgr(vo)
611
712
 
612
- * **범위 검사층이 있는 엔진은 생성 시 범위 밖 값을 거부한다**
613
- (``MIMQE_INVALID_PROPERTY``). 숫자 설정값이든 이름·포트 같은 신원
614
- 항목이든 마찬가지다 - 생성 경로는 저장값과 대조하지 않고 범위만 본다::
713
+ # 7.0.1.3367 -> ILOperationException
714
+ # MIMQE_INVALID_PROPERTY (SOCKTIMEOUTSND must be 1~864000 sec)
715
+ # 7.0.1.3375 -> 성공. 저장값은 조용히 60 이 된다
716
+ # 6.3.1 / 6.3.4 -> 성공. 저장값은 999999 그대로다
615
717
 
616
- vo = ILQmgrProperty("QMGR01", 19999)
617
- vo.setSendTimeout(999999) # 범위 밖 (1~864000)
618
- svc.createQmgr(vo)
619
- # 7.0.1.3367 -> ILOperationException
620
- # MIMQE_INVALID_PROPERTY (SOCKTIMEOUTSND must be 1~864000 sec)
718
+ **그러므로 예외가 없다고 넘긴 값이 들어간 것이 아니다.** 어느 엔진이든
719
+ 안전한 절차는 하나다 - 만든 뒤 :meth:`getQmgrProperty` 로 되읽어
720
+ 값을 확인한다.
621
721
 
622
- * **그 검사층이 없는 구엔진에서는 범위 밖 값이 그대로 저장된다.**
623
- 위와 같은 호출이 6.3.1 / 6.3.4 에서는 오류 없이 성공하고 저장값도
624
- 999999 가 된다. **조용히 잘못된 값으로 도는 쪽은 구엔진이므로**,
625
- 만든 뒤 :meth:`getQmgrProperty` 로 되읽어 확인한다.
722
+ 이름·포트 같은 **신원 항목은 세 엔진 모두에서 거부된다**(규격 밖 포트는
723
+ 어느 빌드에서도 만들어지지 않는다). 그래서 만든 뒤 봐야 할 것은 신원
724
+ 항목이 아니라 **숫자 설정값** 쪽이다.
626
725
 
627
- :meth:`setQmgrProperty` (변경)는 판정 기준이 다르다. 검사층이 있는
628
- 엔진은 **저장값과 대조**해서, 손대지 않은 왕복은 통과시키고 범위 밖
629
- **새** 값만 거부한다 - 구엔진 설정 이관이 막히지 않게 하려는 것이다.
726
+ :meth:`setQmgrProperty` (변경)는 기준이 다르다. 검사층이 있는 엔진은
727
+ **저장값과 대조**해서 손대지 않은 왕복은 통과시키고 범위 밖 **새** 값은
728
+ 거부한다 - 구엔진 설정 이관이 막히지 않게 하려는 것이다.
630
729
 
631
730
  Note:
632
731
  만든 뒤 :meth:`startQmgr` 로 기동해야 접속할 수 있다.
@@ -1322,12 +1421,21 @@ class ILAdminService:
1322
1421
 
1323
1422
  Args:
1324
1423
  qmgrName (str): 기동할 큐 관리자 이름.
1325
- timeout (int): 최대 대기 **초**. 기본 60.
1424
+ timeout (int): 최대 대기 **초**. 1초 간격으로 상태를 확인하며 그
1425
+ 횟수가 이 값이다. **밀리초가 아니다** - 30000 을 넘기면 8시간
1426
+ 넘게 매달린다. 보통 10~60 이면 충분하다.
1326
1427
 
1327
1428
  Returns:
1328
1429
  str: 최종 상태 문자열. **전부 대문자**다 - ``"RUNNING"`` / ``"ERROR"``,
1329
1430
  시간이 다하면 그 시점의 상태(``"STARTING"`` 등).
1330
- **예외가 아니라 문자열로 결과를 알린다** - 값을 반드시 확인하라.
1431
+
1432
+ **실패는 두 가지 방식으로 온다 - 둘 다 대비한다.**
1433
+
1434
+ * 엔진이 기동 실패 사유를 제때 올리면 :class:`ILOperationException`
1435
+ 이 난다(사유가 메시지에 실린다).
1436
+ * 사유가 제때 안 올라오면 예외 없이 ``"ERROR"`` 나 그 시점의 상태
1437
+ 문자열이 돌아온다. 그래서 **반환값을 반드시 확인해야 한다** -
1438
+ 예외가 없다고 기동에 성공한 것이 아니다.
1331
1439
 
1332
1440
  Raises:
1333
1441
  ILOperationException: 기동 요청 자체가 거절됐을 때.
@@ -1388,7 +1496,9 @@ class ILAdminService:
1388
1496
 
1389
1497
  Args:
1390
1498
  qmgrName (str): 정지할 큐 관리자 이름.
1391
- timeout (int): 최대 대기 **초**. 기본 60.
1499
+ timeout (int): 최대 대기 **초**. 1초 간격으로 상태를 확인하며 그
1500
+ 횟수가 이 값이다. **밀리초가 아니다** - 30000 을 넘기면 8시간
1501
+ 넘게 매달린다. 보통 10~60 이면 충분하다.
1392
1502
 
1393
1503
  Returns:
1394
1504
  str: 최종 상태 문자열. **전부 대문자**다 - ``"STOPPED"`` / ``"ERROR"``,
@@ -1518,8 +1628,8 @@ class ILAdminService:
1518
1628
  """
1519
1629
  try:
1520
1630
  ba = bytearray(256)
1521
- ba_merge(keyFileName, ba, 0)
1522
- ba_merge(password, ba, 128)
1631
+ ba_merge(keyFileName, ba, 0, 128)
1632
+ ba_merge(password, ba, 128, 128)
1523
1633
  self.service.apply(
1524
1634
  ILC.MIMQ_SET_KEY_PASSWORD,
1525
1635
  bytes(ba),
@@ -2340,9 +2450,9 @@ class ILAdminService:
2340
2450
  content = fileContent
2341
2451
  ba = bytearray(len(content) + ILC.ILINKBYTE256)
2342
2452
  offset = 0
2343
- ba_merge(fileName, ba, offset)
2453
+ ba_merge(fileName, ba, offset, 128)
2344
2454
  offset += 128
2345
- ba_merge(password, ba, offset)
2455
+ ba_merge(password, ba, offset, 128)
2346
2456
  offset += 128
2347
2457
  ba[offset : offset + len(content)] = content
2348
2458
  self.service.apply(
@@ -2777,8 +2887,8 @@ class ILAdminQmgr:
2777
2887
  )
2778
2888
  try:
2779
2889
  ba = bytearray(ILC.MIMQ_LONG_REQUEST_ARG_LENGTH + len(homePath))
2780
- ba_merge(self.qmgrName, ba, 0)
2781
- ba_merge(homePath, ba, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
2890
+ ba_merge(self.qmgrName, ba, 0, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
2891
+ ba_merge(homePath, ba, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
2782
2892
  self.service.apply(
2783
2893
  ILC.MIMQ_SET_MANAGER_HOMEPATH,
2784
2894
  bytes(ba),
@@ -5119,12 +5229,12 @@ class ILAdminQmgr:
5119
5229
  )
5120
5230
  buffer = bytearray(buflen)
5121
5231
  offset = 0
5122
- ba_merge(queueName, buffer, offset)
5232
+ ba_merge(queueName, buffer, offset, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
5123
5233
  offset += ILC.MIMQ_LONG_REQUEST_ARG_LENGTH
5124
- ba_merge(len(msg_ids), buffer, offset)
5234
+ ba_merge(len(msg_ids), buffer, offset, ILC.SZ_LEN)
5125
5235
  offset += ILC.SZ_LEN
5126
5236
  for msg_id in msg_ids:
5127
- ba_merge(msg_id, buffer, offset)
5237
+ ba_merge(msg_id, buffer, offset, ILC.ILINKBYTE48)
5128
5238
  offset += ILC.ILINKBYTE48
5129
5239
  self.service.apply(
5130
5240
  self.qmgrName,
@@ -742,6 +742,18 @@ class ILC:
742
742
  #: 그 자리는 구엔진의 ``volumeMonitorForceReloadSize`` 다 - 값을 실어 보내면
743
743
  #: 구엔진 imqlsr 가 SIGFPE 로 죽는다(6.3.1.2813 실측).
744
744
  TOPIC_LOGGER_MIN_REVISION = 3283
745
+
746
+ #: ``getLastDiag`` 가 쓰는 진단 조회(3394)가 들어온 엔진 리비전.
747
+ #: 그 미만 엔진은 이 코드를 모르고 ``MIMQE_INVALID_REQ_CODE`` 를
748
+ #: **정상 응답 모양으로** 돌려주므로, 예외가 아니라 조용한 오독이 된다.
749
+ LAST_DIAG_MIN_REVISION = 3380
750
+
751
+ #: 큐 관리자 마지막 진단 조회 요청 코드.
752
+ MIMQ_GET_LAST_DIAG = 3394
753
+ #: 진단 조회 성공 응답 코드.
754
+ MIMQ_SUCCESSFUL_GET_LAST_DIAG = 4276
755
+ #: 진단 조회 실패 응답 코드(없는 큐 관리자 등).
756
+ MIMQ_NOT_SUCCESSFUL_GET_LAST_DIAG = 4277
745
757
  #: 구버전(4096) 블롭에서 공통 접두가 끝나고 레이아웃이 갈리는 지점.
746
758
  QUEUE_LEGACY_DIVERGE_OFFSET = 1772
747
759
  #: 구버전(4096) 블롭에서 incomingDelayDepth 가 있는 위치. 현행과 같다.