From ce19e385f6a6bfb5d76dd807c57d452f1f360776 Mon Sep 17 00:00:00 2001 From: Tommaso Ascani Date: Tue, 17 Sep 2024 16:13:18 +0200 Subject: [PATCH] Wrappers implemented and docs updated --- .../usr/local/agent/pypkg/agent/__init__.py | 52 +++++++++++++ .../node/actions/allocate-ports/50allocate | 2 +- .../actions/deallocate-ports/50deallocate | 2 +- docs/core/port_allocation.md | 53 ++++++++++++++ docs/core/subscription.md | 1 + docs/modules/port_allocation.md | 73 +++++++++---------- 6 files changed, 141 insertions(+), 42 deletions(-) create mode 100644 docs/core/port_allocation.md diff --git a/core/imageroot/usr/local/agent/pypkg/agent/__init__.py b/core/imageroot/usr/local/agent/pypkg/agent/__init__.py index 812107939c..4b6be85b6d 100644 --- a/core/imageroot/usr/local/agent/pypkg/agent/__init__.py +++ b/core/imageroot/usr/local/agent/pypkg/agent/__init__.py @@ -607,3 +607,55 @@ def get_bound_domain_list(rdb, module_id=None): return rval.split() else: return [] + +def allocate_ports(ports_number: int, module_id: str, protocol: str): + """ + Allocate a range of ports for a given module, + if it is already allocated it is deallocated first. + + :param ports_number: Number of consecutive ports required. + :param module_id: Name of the module requesting the ports. + :param protocol: Protocol type ('tcp' or 'udp'). + :return: A tuple (start_port, end_port) if allocation is successful, None otherwise. + """ + + node_id = os.environ['NODE_ID'] + response = agent.tasks.run( + agent_id=f'node/{node_id}', + action='allocate-ports', + data={ + 'ports': ports_number, + 'module_id': module_id, + 'protocol': protocol + } + ) + + if response['exit_code'] != 0: + raise Exception(f"{response['error']}") + + return response['output'] + + +def deallocate_ports(module_id: str, protocol: str): + """ + Deallocate the ports for a given module. + + :param module_id: Name of the module whose ports are to be deallocated. + :param protocol: Protocol type ('tcp' or 'udp'). + :return: A tuple (start_port, end_port) if deallocation is successful, None otherwise. + """ + + node_id = os.environ['NODE_ID'] + response = agent.tasks.run( + agent_id=f'node/{node_id}', + action='deallocate-ports', + data={ + 'module_id': module_id, + 'protocol': protocol + } + ) + + if response['exit_code'] != 0: + raise Exception(f"{response['error']}") + + return response['output'] diff --git a/core/imageroot/var/lib/nethserver/node/actions/allocate-ports/50allocate b/core/imageroot/var/lib/nethserver/node/actions/allocate-ports/50allocate index 108a0c8f68..18398f3b38 100755 --- a/core/imageroot/var/lib/nethserver/node/actions/allocate-ports/50allocate +++ b/core/imageroot/var/lib/nethserver/node/actions/allocate-ports/50allocate @@ -16,7 +16,7 @@ request = json.load(sys.stdin) module_env = os.getenv("AGENT_TASK_USER") if module_env != "" and module_env != f"module/{request['module_id']}": - print(agent.SD_ERR + f"Agent {module_env} does not have permission to change the port allocation for {request['module_id']}.") + print(agent.SD_ERR + f" Agent {module_env} does not have permission to change the port allocation for {request['module_id']}.", file=sys.stderr) sys.exit(1) range = node.ports_manager.allocate_ports(int(request['ports']), request['module_id'], request['protocol']) diff --git a/core/imageroot/var/lib/nethserver/node/actions/deallocate-ports/50deallocate b/core/imageroot/var/lib/nethserver/node/actions/deallocate-ports/50deallocate index 8918804604..8e7d5161e6 100755 --- a/core/imageroot/var/lib/nethserver/node/actions/deallocate-ports/50deallocate +++ b/core/imageroot/var/lib/nethserver/node/actions/deallocate-ports/50deallocate @@ -16,7 +16,7 @@ request = json.load(sys.stdin) module_env = os.getenv("AGENT_TASK_USER") if module_env != "" and module_env != f"module/{request['module_id']}": - print(agent.SD_ERR + f"Agent {module_env} does not have permission to change the port allocation for {request['module_id']}.") + print(agent.SD_ERR + f"Agent {module_env} does not have permission to change the port allocation for {request['module_id']}.", file=sys.stderr) sys.exit(1) range = node.ports_manager.deallocate_ports(request['module_id'], request['protocol']) diff --git a/docs/core/port_allocation.md b/docs/core/port_allocation.md new file mode 100644 index 0000000000..f215ca1b95 --- /dev/null +++ b/docs/core/port_allocation.md @@ -0,0 +1,53 @@ +--- +layout: default +title: Port allocation +nav_order: 17 +parent: Core +--- + +## Importing the Library + +To use the `ports_manager` library, you need to import it into your Python script as follows: + +```python +import node.ports_manager +``` + +## Available Functions + +### `allocate_ports` + +This function allows you to allocate a specific number of ports for a given module and protocol. + +- **Parameters**: + - `required_ports` (*int*): The number of ports required. + - `module_name` (*str*): The name of the module requesting the ports. + - `protocol` (*str*): The protocol for which the ports are required (e.g. "tcp" or "udp"). + +- **Usage Example**: + +```python +allocated_ports = node.ports_manager.allocate_ports(5, "my_module", "tcp") +print(f"my_module ports allocated: {allocated_ports}") +``` + +### `deallocate_ports` + +This function allows you to deallocate all ports previously assigned to a specific module for a given protocol. + +- **Parameters**: + - `module_name` (*str*): The name of the module for which ports should be deallocated. + - `protocol` (*str*): The protocol for which the ports were allocated (e.g., "tcp" or "udp"). + +- **Usage Example**: + +```python +deallocated_ports = node.ports_manager.deallocate_ports("my_module", "udp") +print(f"my_module ports deallocated: {deallocated_ports}") +``` + +## Additional Notes + +- Ensure to handle exceptions that may be raised during the allocation or deallocation of ports. +- Ports allocated will remain reserved for the specified module until they are explicitly deallocated. +- When using the `allocate_ports` function, if the module already has allocated ports, they will first be deallocated and then reallocated. \ No newline at end of file diff --git a/docs/core/subscription.md b/docs/core/subscription.md index 6add9d34cb..556913afb0 100644 --- a/docs/core/subscription.md +++ b/docs/core/subscription.md @@ -1,6 +1,7 @@ --- layout: default title: Subscription +nav_order: 16 parent: Core --- diff --git a/docs/modules/port_allocation.md b/docs/modules/port_allocation.md index 8dd2ccca82..b91a0322ae 100644 --- a/docs/modules/port_allocation.md +++ b/docs/modules/port_allocation.md @@ -24,63 +24,56 @@ The available environment variables will be: - `TCP_PORTS`, `UDP_PORTS`: only if value is greater than 1 and less or equal than 8, it contains a comma separated list of ports like, i.e. `20001,20002,20003` -Currently, allocated ports are saved in an SQLite database file located in the node working directory. +Currently, allocated ports are saved in an SQLite database file managed by the local node agent. -The module requires an additional role to manage its port allocation, which is assigned by setting the `org.nethserver.authorizations` label on the module image, as shown in the following example: -``` -org.nethserver.authorizations = node:portsadm -``` - -The module will be granted execution permissions for the following actions on the local node: -- `allocate-ports` -- `deallocate-ports` +## Agent library -The `ports_manager` library provides functions for managing network ports used by different modules within an application. You can dynamically allocate and deallocate ports based on the module's requirements. +The Python `agent` library provides a convenient interface for managing port allocation and deallocation, based on the node actions `allocate_ports` and `deallocate_ports`. -## Importing the Library +It is recommended to use `os.environ['MODULE_ID']` to ensure the correct module name is used, as calling the function with a name that does not correspond to the invoking module will result in an exception. -To use the `ports_manager` library, you need to import it into your Python script as follows: +### Allocate ports -```python -import node.ports_manager -``` +Imagine an application module that initially requires only one TCP port. Later, a new feature is added, and it needs four TCP ports to handle more connections. -## Available Functions +If ports are already allocated for this module, the previous allocation will be deallocated, and the new requested range of ports will be allocated. Here’s how this can be done: -### `allocate_ports` +```python +import agent +import os -This function allows you to allocate a specific number of ports for a given module and protocol. +# Allocate 4 TCP ports for the "my_module" module +allocated_ports = agent.allocate_ports(4, os.environ['MODULE_ID'], "tcp") +print(f"Allocated TCP ports: {allocated_ports}") +``` -- **Parameters**: - - `required_ports` (*int*): The number of ports required. - - `module_name` (*str*): The name of the module requesting the ports. - - `protocol` (*str*): The protocol for which the ports are required (e.g. "tcp" or "udp"). +### Deallocate ports -- **Usage Example**: +If the module no longer needs the allocated ports, such as when a feature is removed or disabled, the ports can be easily deallocated: ```python -allocated_ports = node.ports_manager.allocate_ports(5, "my_module", "tcp") -print(f"my_module ports allocated: {allocated_ports}") +import agent +import os + +# Deallocate UDP ports for the "my_module" module +deallocated_ports = agent.deallocate_ports(os.environ['MODULE_ID'], "udp") +print(f"Deallocated UDP ports: {deallocated_ports}") ``` +By deallocating the ports, the module frees up the resources, allowing other modules to use those ports. -### `deallocate_ports` +For more information about functions, see [Port allocation](../../core/port_allocation) -This function allows you to deallocate all ports previously assigned to a specific module for a given protocol. +These functions dynamically allocate and deallocate ports based on the module's needs without requiring direct interaction with the node's APIs. -- **Parameters**: - - `module_name` (*str*): The name of the module for which ports should be deallocated. - - `protocol` (*str*): The protocol for which the ports were allocated (e.g., "tcp" or "udp"). +## Authorizations -- **Usage Example**: +The module requires an additional role to manage port allocation, which is assigned by setting the `org.nethserver.authorizations` label on the module image, as shown in the following example: -```python -deallocated_ports = node.ports_manager.deallocate_ports("my_module", "udp") -print(f"my_module ports deallocated: {deallocated_ports}") ``` +org.nethserver.authorizations = node:portsadm +``` +The module will be granted execution permissions for the following actions on the local node: +- `allocate-ports` +- `deallocate-ports` -## Additional Notes - -- Ensure to handle exceptions that may be raised during the allocation or deallocation of ports. -- Ports allocated will remain reserved for the specified module until they are explicitly deallocated. -- When using the `allocate_ports` function, if the module already has allocated ports, they will first be deallocated and then reallocated. - +However, as mentioned above, these actions can be carried out using the agent library without making direct node API calls.