|
| 1 | +"""recceiver-clean — sweep Active CF channels to Inactive for a given recceiver ID. |
| 2 | +
|
| 3 | +Run after a RecCeiver restart (with cleanOnStart=False) to mark channels Inactive |
| 4 | +whose IOCs have not reconnected. |
| 5 | +
|
| 6 | +Usage: |
| 7 | + recceiver-clean -f /path/to/recceiver.conf [--recceiver-id ID] [--dry-run] |
| 8 | +""" |
| 9 | + |
| 10 | +import argparse |
| 11 | +import configparser |
| 12 | +import logging |
| 13 | +import sys |
| 14 | + |
| 15 | +from requests import RequestException |
| 16 | + |
| 17 | +from recceiver.cf.adapter import PyCFClientAdapter |
| 18 | +from recceiver.cf.config import CFConfig |
| 19 | +from recceiver.cf.model import CFProperty, CFPropertyName, PVStatus |
| 20 | +from recceiver.processors import ConfigAdapter |
| 21 | + |
| 22 | +log = logging.getLogger(__name__) |
| 23 | + |
| 24 | + |
| 25 | +def run_clean(cf_config: CFConfig, client=None, dry_run: bool = False) -> int: |
| 26 | + """Mark all Active channels for cf_config.recceiver_id Inactive. |
| 27 | +
|
| 28 | + Returns the total count of channels swept. Pass a pre-built client for testing. |
| 29 | + """ |
| 30 | + if client is None: |
| 31 | + from channelfinder import ChannelFinderClient |
| 32 | + |
| 33 | + client = PyCFClientAdapter( |
| 34 | + ChannelFinderClient( |
| 35 | + BaseURL=cf_config.base_url, |
| 36 | + username=cf_config.cf_username, |
| 37 | + password=cf_config.cf_password, |
| 38 | + verify_ssl=cf_config.verify_ssl, |
| 39 | + ), |
| 40 | + size_limit=int(cf_config.cf_query_limit), |
| 41 | + ) |
| 42 | + |
| 43 | + total = 0 |
| 44 | + # find_active_for_recceiver is paginated (bounded by size_limit). In live mode each |
| 45 | + # update_property call marks the current page Inactive, so the next query returns a |
| 46 | + # fresh batch; the loop drains all pages. In dry-run mode nothing is deactivated so |
| 47 | + # the same batch would come back on every iteration — break after the first page. |
| 48 | + while True: |
| 49 | + channels = client.find_active_for_recceiver(cf_config.recceiver_id) |
| 50 | + if not channels: |
| 51 | + break |
| 52 | + log.info( |
| 53 | + "Found %d active channels for recceiver_id=%r%s", |
| 54 | + len(channels), |
| 55 | + cf_config.recceiver_id, |
| 56 | + " (dry-run)" if dry_run else "", |
| 57 | + ) |
| 58 | + if not dry_run: |
| 59 | + client.update_property( |
| 60 | + CFProperty(CFPropertyName.PV_STATUS.value, cf_config.username, PVStatus.INACTIVE.value), |
| 61 | + [ch.name for ch in channels], |
| 62 | + ) |
| 63 | + total += len(channels) |
| 64 | + if dry_run: |
| 65 | + break |
| 66 | + return total |
| 67 | + |
| 68 | + |
| 69 | +def main(argv=None): |
| 70 | + parser = argparse.ArgumentParser(description="Mark active CF channels Inactive for a given recceiver ID.") |
| 71 | + parser.add_argument("-f", "--config", required=True, help="Path to recceiver config file") |
| 72 | + parser.add_argument("--recceiver-id", default=None, help="Override recceiver ID from config") |
| 73 | + parser.add_argument("--dry-run", action="store_true", help="Print count without modifying CF") |
| 74 | + args = parser.parse_args(argv) |
| 75 | + |
| 76 | + logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s") |
| 77 | + |
| 78 | + conf = configparser.ConfigParser() |
| 79 | + conf.read(args.config) |
| 80 | + cf_conf = CFConfig.loads(ConfigAdapter(conf, "cf")) |
| 81 | + |
| 82 | + if args.recceiver_id: |
| 83 | + cf_conf.recceiver_id = args.recceiver_id |
| 84 | + |
| 85 | + if cf_conf.base_url is None: |
| 86 | + print("ERROR: baseUrl must be configured in [cf] section", file=sys.stderr) |
| 87 | + sys.exit(1) |
| 88 | + |
| 89 | + try: |
| 90 | + count = run_clean(cf_conf, dry_run=args.dry_run) |
| 91 | + except RequestException as err: |
| 92 | + print(f"ERROR: CF request failed: {err}", file=sys.stderr) |
| 93 | + sys.exit(2) |
| 94 | + |
| 95 | + action = "Would mark" if args.dry_run else "Marked" |
| 96 | + print(f"{action} {count} channels Inactive for recceiver_id={cf_conf.recceiver_id!r}") |
| 97 | + |
| 98 | + |
| 99 | +if __name__ == "__main__": |
| 100 | + main() |
0 commit comments