X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=src%2Fbindings%2Frmr-python%2Frmr%2Frmr.py;h=413d8dcbdaeb042695fd5bed863316e0df9a1e79;hb=839fc1e9a0f53f3cb9e1991d364e593047656584;hp=984ac3107f8851b420714a61222c235086691717;hpb=91aa7028e6bb9e16c16d119cec608c8395e8e322;p=ric-plt%2Flib%2Frmr.git diff --git a/src/bindings/rmr-python/rmr/rmr.py b/src/bindings/rmr-python/rmr/rmr.py index 984ac31..413d8dc 100644 --- a/src/bindings/rmr-python/rmr/rmr.py +++ b/src/bindings/rmr-python/rmr/rmr.py @@ -1,6 +1,7 @@ +# vim: expandtab ts=4 sw=4: # ================================================================================== -# Copyright (c) 2019 Nokia -# Copyright (c) 2018-2019 AT&T Intellectual Property. +# Copyright (c) 2019-2020 Nokia +# Copyright (c) 2018-2020 AT&T Intellectual Property. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,14 +20,14 @@ import json from ctypes import RTLD_GLOBAL, Structure, c_int, POINTER, c_char, c_char_p, c_void_p, memmove, cast from ctypes import CDLL from ctypes import create_string_buffer +from rmr.exceptions import BadBufferAllocation, MeidSizeOutOfRange, InitFailed # https://docs.python.org/3.7/library/ctypes.html # https://stackoverflow.com/questions/2327344/ctypes-loading-a-c-shared-library-that-has-dependencies/30845750#30845750 # make sure you do a set -x LD_LIBRARY_PATH /usr/local/lib/; # even though we don't use these directly, they contain symbols we need -_ = CDLL("libnng.so", mode=RTLD_GLOBAL) -rmr_c_lib = CDLL("librmr_nng.so", mode=RTLD_GLOBAL) +rmr_c_lib = CDLL("librmr_si.so", mode=RTLD_GLOBAL) # Internal Helpers (not a part of public api) @@ -69,7 +70,7 @@ def _get_mapping_dict(cache={}): RMR_ERR_TIMEOUT 12 message processing call timed out RMR_ERR_UNSET 13 the message hasn't been populated with a transport buffer RMR_ERR_TRUNC 14 received message likely truncated - RMR_ERR_INITFAILED 15 initialisation of something (probably message) failed + RMR_ERR_INITFAILED 15 initialization of something (probably message) failed """ if cache: @@ -93,6 +94,9 @@ def _state_to_status(stateno): return sdict.get(stateno, "UNKNOWN STATE") +_RCONST = _get_constants() + + ############## # PUBLIC API ############## @@ -100,7 +104,13 @@ def _state_to_status(stateno): # These constants are directly usable by importers of this library # TODO: Are there others that will be useful? -RMR_MAX_RCV_BYTES = _get_constants()["RMR_MAX_RCV_BYTES"] + +RMR_MAX_RCV_BYTES = _RCONST["RMR_MAX_RCV_BYTES"] +RMRFL_MTCALL = _RCONST.get("RMRFL_MTCALL", 0x02) # initialization flags +RMRFL_NONE = _RCONST.get("RMRFL_NONE", 0x0) +RMR_OK = _RCONST["RMR_OK"] # useful state constants +RMR_ERR_TIMEOUT = _RCONST["RMR_ERR_TIMEOUT"] +RMR_ERR_RETRY = _RCONST["RMR_ERR_RETRY"] class rmr_mbuf_t(Structure): @@ -158,8 +168,14 @@ def rmr_init(uproto_port, max_msg_size, flags): """ Refer to rmr C documentation for rmr_init extern void* rmr_init(char* uproto_port, int max_msg_size, int flags) + + This python function checks that the context is not None and raises + an excption if it is. """ - return _rmr_init(uproto_port, max_msg_size, flags) + mrc = _rmr_init(uproto_port, max_msg_size, flags) + if mrc is None: + raise InitFailed() + return mrc _rmr_ready = rmr_c_lib.rmr_ready @@ -205,12 +221,76 @@ _rmr_alloc_msg.argtypes = [c_void_p, c_int] _rmr_alloc_msg.restype = POINTER(rmr_mbuf_t) -def rmr_alloc_msg(vctx, size): +def rmr_alloc_msg( + vctx, size, payload=None, gen_transaction_id=False, mtype=None, meid=None, sub_id=None, fixed_transaction_id=None +): """ Refer to the rmr C documentation for rmr_alloc_msg extern rmr_mbuf_t* rmr_alloc_msg(void* vctx, int size) + TODO: on next API break, clean up transaction_id ugliness. Kept for now to preserve API. + + if payload is not None, attempts to set the payload + if gen_transaction_id is True, it generates and sets a transaction id. Note, fixed_transaction_id supersedes this option + if mtype is not None, sets the sbuf's message type + if meid is not None, sets the sbuf's meid + if sub_id is not None, sets the sbud's subscription id + if fixed_transaction_id is set, it deterministically sets the transaction_id. This overrides the option gen_transation_id + """ - return rmr_alloc_msg(vctx, size) + sbuf = _rmr_alloc_msg(vctx, size) + try: + # make sure the alloc worked + sbuf.contents + + # set specified fields + if payload: + set_payload_and_length(payload, sbuf) + + if fixed_transaction_id: + set_transaction_id(sbuf, fixed_transaction_id) + elif gen_transaction_id: + generate_and_set_transaction_id(sbuf) + + if mtype: + sbuf.contents.mtype = mtype + + if meid: + rmr_set_meid(sbuf, meid) + + if sub_id: + sbuf.contents.sub_id = sub_id + + return sbuf + + except ValueError: + raise BadBufferAllocation + + +_rmr_realloc_payload = rmr_c_lib.rmr_realloc_payload +_rmr_realloc_payload.argtypes = [POINTER(rmr_mbuf_t), c_int, c_int, c_int] # new_len, copy, clone +_rmr_realloc_payload.restype = POINTER(rmr_mbuf_t) + + +def rmr_realloc_payload(ptr_mbuf, new_len, copy=False, clone=False): + """ + Refer to the rmr C documentation for rmr_realloc_payload(). + extern rmr_mbuf_t* rmr_realloc_payload(rmr_mbuf_t*, int, int, int) + """ + return _rmr_realloc_payload(ptr_mbuf, new_len, copy, clone) + + +_rmr_free_msg = rmr_c_lib.rmr_free_msg +_rmr_free_msg.argtypes = [c_void_p] +_rmr_free_msg.restype = None + + +def rmr_free_msg(mbuf): + """ + Refer to the rmr C documentation for rmr_free_msg + extern void rmr_free_msg(rmr_mbuf_t* mbuf ) + """ + if mbuf is not None: + _rmr_free_msg(mbuf) _rmr_payload_size = rmr_c_lib.rmr_payload_size @@ -275,11 +355,22 @@ _rmr_rts_msg.argtypes = [c_void_p, POINTER(rmr_mbuf_t)] _rmr_rts_msg.restype = POINTER(rmr_mbuf_t) -def rmr_rts_msg(vctx, ptr_mbuf): +def rmr_rts_msg(vctx, ptr_mbuf, payload=None, mtype=None): """ Refer to the rmr C documentation for rmr_rts_msg extern rmr_mbuf_t* rmr_rts_msg(void* vctx, rmr_mbuf_t* msg) + + additional features beyond c-rmr: + if payload is not None, attempts to set the payload + if mtype is not None, sets the sbuf's message type """ + + if payload: + set_payload_and_length(payload, ptr_mbuf) + + if mtype: + ptr_mbuf.contents.mtype = mtype + return _rmr_rts_msg(vctx, ptr_mbuf) @@ -301,30 +392,53 @@ _rmr_bytes2meid.argtypes = [POINTER(rmr_mbuf_t), c_char_p, c_int] _rmr_bytes2meid.restype = c_int -def rmr_bytes2meid(ptr_mbuf, src, length): +def rmr_set_meid(ptr_mbuf, byte_str): """ Refer to the rmr C documentation for rmr_bytes2meid extern int rmr_bytes2meid(rmr_mbuf_t* mbuf, unsigned char const* src, int len); + + Caution: the meid length supported in an RMR message is 32 bytes, but C applications + expect this to be a nil terminated string and thus only 31 bytes are actually available. + + Raises: exceptions.MeidSizeOutOfRang """ - return _rmr_bytes2meid(ptr_mbuf, src, length) + max = _get_constants().get("RMR_MAX_MEID", 32) + if len(byte_str) >= max: + raise MeidSizeOutOfRange + + return _rmr_bytes2meid(ptr_mbuf, byte_str, len(byte_str)) # CAUTION: Some of the C functions expect a mutable buffer to copy the bytes into; # if there is a get_* function below, use it to set up and return the # buffer properly. - +# extern unsigned char* rmr_get_meid(rmr_mbuf_t* mbuf, unsigned char* dest); +# we don't provide direct access to this function (unless it is asked for) because it is not really useful to provide your own buffer. +# Rather, rmr_get_meid does this for you, and just returns the string. _rmr_get_meid = rmr_c_lib.rmr_get_meid _rmr_get_meid.argtypes = [POINTER(rmr_mbuf_t), c_char_p] _rmr_get_meid.restype = c_char_p -def rmr_get_meid(ptr_mbuf, dest): +def rmr_get_meid(ptr_mbuf): """ - Refer to the rmr C documentation for rmr_get_meid - extern unsigned char* rmr_get_meid(rmr_mbuf_t* mbuf, unsigned char* dest); + Get the managed equipment ID (meid) from the message header. + + Parameters + ---------- + ptr_mbuf: ctypes c_void_p + Pointer to an rmr message buffer + + Returns + ------- + string: + meid """ - return _rmr_get_meid(ptr_mbuf, dest) + sz = _get_constants().get("RMR_MAX_MEID", 32) # size for buffer to fill + buf = create_string_buffer(sz) + _rmr_get_meid(ptr_mbuf, buf) + return buf.value _rmr_get_src = rmr_c_lib.rmr_get_src @@ -397,15 +511,8 @@ def message_summary(ptr_mbuf): dict: dict message summary """ - if ptr_mbuf.contents.len > RMR_MAX_RCV_BYTES: - return "Malformed message: message length is greater than the maximum possible" - - meid = get_meid(ptr_mbuf) - if meid == "\000" * _get_constants().get("RMR_MAX_MEID", 32): # special case all nils - meid = None - return { - "payload": get_payload(ptr_mbuf), + "payload": get_payload(ptr_mbuf) if ptr_mbuf.contents.state == RMR_OK else None, "payload length": ptr_mbuf.contents.len, "message type": ptr_mbuf.contents.mtype, "subscription id": ptr_mbuf.contents.sub_id, @@ -413,7 +520,7 @@ def message_summary(ptr_mbuf): "message state": ptr_mbuf.contents.state, "message status": _state_to_status(ptr_mbuf.contents.state), "payload max size": rmr_payload_size(ptr_mbuf), - "meid": meid, + "meid": rmr_get_meid(ptr_mbuf), "message source": get_src(ptr_mbuf), "errno": ptr_mbuf.contents.tp_state, } @@ -431,44 +538,39 @@ def set_payload_and_length(byte_str, ptr_mbuf): ptr_mbuf: ctypes c_void_p Pointer to an rmr message buffer """ + if rmr_payload_size(ptr_mbuf) < len(byte_str): # existing message payload too small + ptr_mbuf = rmr_realloc_payload(ptr_mbuf, len(byte_str), True) + memmove(ptr_mbuf.contents.payload, byte_str, len(byte_str)) ptr_mbuf.contents.len = len(byte_str) def generate_and_set_transaction_id(ptr_mbuf): """ - | Generate a UUID and Set an rmr transaction id to it - | In place method, no return + Generate a UUID and Set an rmr transaction id to it Parameters ---------- ptr_mbuf: ctypes c_void_p Pointer to an rmr message buffer """ - uu_id = uuid.uuid1().hex.encode("utf-8") - sz = _get_constants().get("RMR_MAX_XID", 0) - memmove(ptr_mbuf.contents.xaction, uu_id, sz) + set_transaction_id(ptr_mbuf, uuid.uuid1().hex.encode("utf-8")) -def get_meid(ptr_mbuf): +def set_transaction_id(ptr_mbuf, tid_bytes): """ - | Get the managed equipment ID (meid) from the message header. - | This is a 32 byte field and RMr returns all 32 bytes which if the sender did not set will be garbage. + Set an rmr transaction id + TODO: on next API break, merge these two functions. Not done now to preserve API. Parameters ---------- ptr_mbuf: ctypes c_void_p Pointer to an rmr message buffer - - Returns - ------- - string: - meid + tid_bytes: bytes + bytes of the desired transaction id """ - sz = _get_constants().get("RMR_MAX_MEID", 64) # size for buffer to fill - buf = create_string_buffer(sz) - rmr_get_meid(ptr_mbuf, buf) - return buf.raw.decode() + sz = _get_constants().get("RMR_MAX_XID", 0) + memmove(ptr_mbuf.contents.xaction, tid_bytes, sz) def get_src(ptr_mbuf): @@ -483,7 +585,7 @@ def get_src(ptr_mbuf): Returns ------- string: - meid + message source """ sz = _get_constants().get("RMR_MAX_SRC", 64) # size to fill buf = create_string_buffer(sz)