jetstream-api 0.6.5__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.5 → jetstream_api-0.6.7}/PKG-INFO +1 -1
  2. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/__init__.py +2 -1
  3. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/admin.py +141 -34
  4. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/common.py +12 -0
  5. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/msg.py +2668 -2668
  6. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/properties.py +369 -215
  7. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/qmgr.py +5 -1
  8. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/util.py +28 -2
  9. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/jetstream_api.egg-info/PKG-INFO +1 -1
  10. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/pyproject.toml +1 -1
  11. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/LICENSE.txt +0 -0
  12. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/README.md +0 -0
  13. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/admintopic.py +0 -0
  14. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/connectivity.py +0 -0
  15. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/exception.py +0 -0
  16. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/pattern.py +0 -0
  17. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/producer.py +0 -0
  18. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/py.typed +0 -0
  19. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/ilink/topic.py +0 -0
  20. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/jetstream_api.egg-info/SOURCES.txt +0 -0
  21. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/jetstream_api.egg-info/dependency_links.txt +0 -0
  22. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/jetstream_api.egg-info/requires.txt +0 -0
  23. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/jetstream_api.egg-info/top_level.txt +0 -0
  24. {jetstream_api-0.6.5 → jetstream_api-0.6.7}/setup.cfg +0 -0
  25. {jetstream_api-0.6.5 → 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.5
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.5"
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),
@@ -605,31 +701,31 @@ class ILAdminService:
605
701
  ILException: 통신 실패.
606
702
 
607
703
  Warning:
608
- **속성 객체를 직접 채워 넘길 때만 해당한다.** 이름/포트만 넘기는
609
- 호출은 나머지가 기본값으로 채워지므로 조용히 바뀔 값이 없고, 넘기는
610
- 이름·포트·홈경로는 모두 신원 항목이라 규격을 벗어나면 **거부된다** -
611
- 모르고 지나갈 수 없다.
704
+ **값 검증은 엔진이 한다.** 이 API 는 문자열 길이(와이어 폭) 외의 값
705
+ 범위를 검사하지 않는다. 그래서 **범위 밖 숫자를 넘겼을 때 무슨 일이
706
+ 일어나는지는 상대 엔진에 달려 있고, 결과가 셋으로 갈린다.** 셋 다
707
+ 현재 운영 중인 엔진에서 실제로 나온다::
612
708
 
613
- :class:`ILQmgrProperty` 를 채워 넘기면 다르다. **값 검증은 엔진이
614
- 하는데**(이 API 는 문자열 길이 외의 범위를 검사하지 않는다) 엔진의
615
- 처리가 **생성과 변경에서 다르다.**
709
+ vo = ILQmgrProperty("QMGR01", 19999)
710
+ vo.setSendTimeout(999999) # 범위 밖 (규격 1~864000)
711
+ svc.createQmgr(vo)
616
712
 
617
- * **숫자 설정값**(타임아웃/주기/보관 수 등)이 범위를 벗어나면 엔진
618
- 7.1.1 이상은 거부하지 않고 **조용히 기본값으로 바꿔서 만든다.**
619
- 대조할 저장값이 없어 판정할 수 없기 때문이다. **오타를 내도
620
- 만들기는 성공하므로** 만든 뒤 :meth:`getQmgrProperty` 로 값을
621
- 확인한다::
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 그대로다
622
717
 
623
- vo = ILQmgrProperty("QMGR01", 19999)
624
- vo.setSendTimeout(999999) # 범위 밖 (1~864000)
625
- svc.createQmgr(vo) # 성공한다 - 값은 기본값 60 이 된다
718
+ **그러므로 예외가 없다고 넘긴 값이 들어간 것이 아니다.** 어느 엔진이든
719
+ 안전한 절차는 하나다 - 만든 뒤 :meth:`getQmgrProperty` 로 되읽어
720
+ 값을 확인한다.
626
721
 
627
- * 그래서 만든 뒤 봐야 할 것은 신원 항목이 아니라 숫자 설정값 쪽이다.
628
- * **엔진 7.1.1 미만에는 이 검사층 자체가 없다.** 어떤 값도 걸러지지
629
- 않으므로 호출자가 스스로 확인해야 한다.
722
+ 이름·포트 같은 **신원 항목은 세 엔진 모두에서 거부된다**(규격 밖 포트는
723
+ 어느 빌드에서도 만들어지지 않는다). 그래서 만든 뒤 봐야 할 것은 신원
724
+ 항목이 아니라 **숫자 설정값** 쪽이다.
630
725
 
631
- :meth:`setQmgrProperty` (변경)는 다르다 - 저장값과 다른 범위 밖
632
- 값은 거부된다.
726
+ :meth:`setQmgrProperty` (변경)는 기준이 다르다. 검사층이 있는 엔진은
727
+ **저장값과 대조**해서 손대지 않은 왕복은 통과시키고 범위 밖 **새** 값은
728
+ 거부한다 - 구엔진 설정 이관이 막히지 않게 하려는 것이다.
633
729
 
634
730
  Note:
635
731
  만든 뒤 :meth:`startQmgr` 로 기동해야 접속할 수 있다.
@@ -1325,12 +1421,21 @@ class ILAdminService:
1325
1421
 
1326
1422
  Args:
1327
1423
  qmgrName (str): 기동할 큐 관리자 이름.
1328
- timeout (int): 최대 대기 **초**. 기본 60.
1424
+ timeout (int): 최대 대기 **초**. 1초 간격으로 상태를 확인하며 그
1425
+ 횟수가 이 값이다. **밀리초가 아니다** - 30000 을 넘기면 8시간
1426
+ 넘게 매달린다. 보통 10~60 이면 충분하다.
1329
1427
 
1330
1428
  Returns:
1331
1429
  str: 최종 상태 문자열. **전부 대문자**다 - ``"RUNNING"`` / ``"ERROR"``,
1332
1430
  시간이 다하면 그 시점의 상태(``"STARTING"`` 등).
1333
- **예외가 아니라 문자열로 결과를 알린다** - 값을 반드시 확인하라.
1431
+
1432
+ **실패는 두 가지 방식으로 온다 - 둘 다 대비한다.**
1433
+
1434
+ * 엔진이 기동 실패 사유를 제때 올리면 :class:`ILOperationException`
1435
+ 이 난다(사유가 메시지에 실린다).
1436
+ * 사유가 제때 안 올라오면 예외 없이 ``"ERROR"`` 나 그 시점의 상태
1437
+ 문자열이 돌아온다. 그래서 **반환값을 반드시 확인해야 한다** -
1438
+ 예외가 없다고 기동에 성공한 것이 아니다.
1334
1439
 
1335
1440
  Raises:
1336
1441
  ILOperationException: 기동 요청 자체가 거절됐을 때.
@@ -1391,7 +1496,9 @@ class ILAdminService:
1391
1496
 
1392
1497
  Args:
1393
1498
  qmgrName (str): 정지할 큐 관리자 이름.
1394
- timeout (int): 최대 대기 **초**. 기본 60.
1499
+ timeout (int): 최대 대기 **초**. 1초 간격으로 상태를 확인하며 그
1500
+ 횟수가 이 값이다. **밀리초가 아니다** - 30000 을 넘기면 8시간
1501
+ 넘게 매달린다. 보통 10~60 이면 충분하다.
1395
1502
 
1396
1503
  Returns:
1397
1504
  str: 최종 상태 문자열. **전부 대문자**다 - ``"STOPPED"`` / ``"ERROR"``,
@@ -1521,8 +1628,8 @@ class ILAdminService:
1521
1628
  """
1522
1629
  try:
1523
1630
  ba = bytearray(256)
1524
- ba_merge(keyFileName, ba, 0)
1525
- ba_merge(password, ba, 128)
1631
+ ba_merge(keyFileName, ba, 0, 128)
1632
+ ba_merge(password, ba, 128, 128)
1526
1633
  self.service.apply(
1527
1634
  ILC.MIMQ_SET_KEY_PASSWORD,
1528
1635
  bytes(ba),
@@ -2343,9 +2450,9 @@ class ILAdminService:
2343
2450
  content = fileContent
2344
2451
  ba = bytearray(len(content) + ILC.ILINKBYTE256)
2345
2452
  offset = 0
2346
- ba_merge(fileName, ba, offset)
2453
+ ba_merge(fileName, ba, offset, 128)
2347
2454
  offset += 128
2348
- ba_merge(password, ba, offset)
2455
+ ba_merge(password, ba, offset, 128)
2349
2456
  offset += 128
2350
2457
  ba[offset : offset + len(content)] = content
2351
2458
  self.service.apply(
@@ -2780,8 +2887,8 @@ class ILAdminQmgr:
2780
2887
  )
2781
2888
  try:
2782
2889
  ba = bytearray(ILC.MIMQ_LONG_REQUEST_ARG_LENGTH + len(homePath))
2783
- ba_merge(self.qmgrName, ba, 0)
2784
- 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)
2785
2892
  self.service.apply(
2786
2893
  ILC.MIMQ_SET_MANAGER_HOMEPATH,
2787
2894
  bytes(ba),
@@ -5122,12 +5229,12 @@ class ILAdminQmgr:
5122
5229
  )
5123
5230
  buffer = bytearray(buflen)
5124
5231
  offset = 0
5125
- ba_merge(queueName, buffer, offset)
5232
+ ba_merge(queueName, buffer, offset, ILC.MIMQ_LONG_REQUEST_ARG_LENGTH)
5126
5233
  offset += ILC.MIMQ_LONG_REQUEST_ARG_LENGTH
5127
- ba_merge(len(msg_ids), buffer, offset)
5234
+ ba_merge(len(msg_ids), buffer, offset, ILC.SZ_LEN)
5128
5235
  offset += ILC.SZ_LEN
5129
5236
  for msg_id in msg_ids:
5130
- ba_merge(msg_id, buffer, offset)
5237
+ ba_merge(msg_id, buffer, offset, ILC.ILINKBYTE48)
5131
5238
  offset += ILC.ILINKBYTE48
5132
5239
  self.service.apply(
5133
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 가 있는 위치. 현행과 같다.