diff --git a/CHANGELOG.md b/CHANGELOG.md index 486f1ed3..937eade4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - create_ssh_config adds extra indentation (Issue [#300](https://github.com/fabric-testbed/fabrictestbed-extensions/issues/300)) - Remove duplicate Node.delete() method (Issue [#321](https://github.com/fabric-testbed/fabrictestbed-extensions/issues/321)) +## Added +- Missing docstrings in node module (Issue [#318](https://github.com/fabric-testbed/fabrictestbed-extensions/issues/318)) + ## [1.6.4] - 2024-03-05 ### Fixed diff --git a/docs/source/node.rst b/docs/source/node.rst index 2d8e04af..6a8d87bf 100644 --- a/docs/source/node.rst +++ b/docs/source/node.rst @@ -6,4 +6,5 @@ node .. autoclass:: fabrictestbed_extensions.fablib.node.Node :members: + :no-index: :special-members: __str__ diff --git a/fabrictestbed_extensions/fablib/fablib.py b/fabrictestbed_extensions/fablib/fablib.py index 9ae18ba7..175394c7 100644 --- a/fabrictestbed_extensions/fablib/fablib.py +++ b/fabrictestbed_extensions/fablib/fablib.py @@ -676,27 +676,30 @@ def __init__( def validate_config(self): """ - Validate and create Fablib config - checks if all the required configuration exists for slice - provisioning to work successfully + Validate and create Fablib config - checks if all the required + configuration exists for slice provisioning to work + successfully - - Checks Credential Manager Host is configured properly + - Checks Credential Manager Host is configured properly - - Checks Orchestrator Host is configured properly + - Checks Orchestrator Host is configured properly - - Checks Core API Host is configured properly + - Checks Core API Host is configured properly - - Checks Bastion Host is configured properly + - Checks Bastion Host is configured properly - - Check Sliver keys exist; create sliver keys if they do not exist + - Check Sliver keys exist; create sliver keys if they do + not exist - - Check Bastion keys exist and are not expired; update/create bastion keys if expired or do not exist + - Check Bastion keys exist and are not expired; + update/create bastion keys if expired or do not exist - - Check Bastion Username is configured + - Check Bastion Username is configured - - Check Project Id is configured + - Check Project Id is configured + + .. deprecated:: 1.6.5 Use `verify_and_configure()` instead. - .. deprecated:: 1.6.5 - Use `verify_and_configure()` instead. @raises Exception if the configuration is invalid """ warnings.warn( diff --git a/fabrictestbed_extensions/fablib/node.py b/fabrictestbed_extensions/fablib/node.py index 8c66ea3a..5e5ece68 100644 --- a/fabrictestbed_extensions/fablib/node.py +++ b/fabrictestbed_extensions/fablib/node.py @@ -83,6 +83,10 @@ class Node: + """ + A class for working with FABRIC nodes. + """ + default_cores = 2 default_ram = 8 default_disk = 10 @@ -121,6 +125,9 @@ def __init__(self, slice: Slice, node: FimNode): logging.getLogger("paramiko").setLevel(logging.WARNING) def get_fablib_manager(self): + """ + Get a reference to :py:class:`.FablibManager`. + """ return self.slice.get_fablib_manager() def __str__(self): @@ -230,6 +237,11 @@ def toJson(self): @staticmethod def get_pretty_name_dict(): + """ + Return mappings from non-pretty names to pretty names. + + Pretty names are in table headers. + """ return { "id": "ID", "name": "Name", @@ -648,6 +660,9 @@ def combined_filter_function(x): ) def get_networks(self): + """ + Get a list of networks attached to the node. + """ networks = [] for interface in self.get_interfaces(): networks.append(interface.get_network()) @@ -1206,6 +1221,12 @@ def validIPAddress(self, IP: str) -> str: def get_paramiko_key( self, private_key_file: str = None, get_private_key_passphrase: str = None ) -> paramiko.PKey: + """ + Get SSH pubkey, for internal use. + + :return: an SSH pubkey. + :rtype: paramiko.PKey + """ # TODO: This is a bit of a hack and should probably test he keys for their types # rather than relying on execptions if get_private_key_passphrase: @@ -2749,26 +2770,43 @@ def add_storage(self, name: str, auto_mount: bool = False) -> Component: return Component.new_storage(node=self, name=name, auto_mount=auto_mount) def get_fim(self): + """ + Get FABRIC Information Model (fim) object for the node. + """ return self.get_fim_node() def set_user_data(self, user_data: dict): + """ + Set user data. + + :param user_data: a `dict`. + """ self.get_fim().set_property( pname="user_data", pval=UserData(json.dumps(user_data)) ) def get_user_data(self): + """ + Get user data. + """ try: return json.loads(str(self.get_fim().get_property(pname="user_data"))) except: return {} def delete(self): + """ + Remove the node, including components connected to it. + """ for component in self.get_components(): component.delete() self.get_slice().get_fim_topology().remove_node(name=self.get_name()) def init_fablib_data(self): + """ + Initialize fablib data. Called by :py:meth:`new_node()`. + """ fablib_data = { "instantiated": "False", "run_update_commands": "False", @@ -2778,12 +2816,18 @@ def init_fablib_data(self): self.set_fablib_data(fablib_data) def get_fablib_data(self): + """ + Get fablib data. Usually used internally. + """ try: return self.get_user_data()["fablib_data"] except: return {} def set_fablib_data(self, fablib_data: dict): + """ + Set fablib data. Usually used internally. + """ user_data = self.get_user_data() user_data["fablib_data"] = fablib_data self.set_user_data(user_data) @@ -2900,12 +2944,22 @@ def post_boot_tasks(self): return [] def get_routes(self): + """ + .. warning:: + + This method is for fablib internal use, and will be made private in the future. + """ try: return self.get_fablib_data()["routes"] except Exception as e: return [] def config_routes(self): + """ + .. warning:: + + This method is for fablib internal use, and will be made private in the future. + """ routes = self.get_routes() for route in routes: @@ -2931,6 +2985,12 @@ def config_routes(self): self.ip_route_add(subnet=ipaddress.ip_network(subnet), gateway=next_hop) def run_post_boot_tasks(self, log_dir: str = "."): + """ + Run post-boot tasks. Called by :py:meth:`config()`. + + Post-boot tasks are list of commands associated with + `post_boot_tasks` in fablib data. + """ logging.debug(f"run_post_boot_tasks: {self.get_name()}") fablib_data = self.get_fablib_data() if "post_boot_tasks" in fablib_data: @@ -2965,6 +3025,12 @@ def run_post_boot_tasks(self, log_dir: str = "."): logging.error(f"Invalid post boot command: {command}") def run_post_update_commands(self, log_dir: str = "."): + """ + Run post-update commands. Called by :py:meth:`config()`. + + Post-update commands are list of commands associated with + `post_update_commands` in fablib data. + """ fablib_data = self.get_fablib_data() if "post_update_commands" in fablib_data: commands = fablib_data["post_update_commands"] @@ -2977,6 +3043,9 @@ def run_post_update_commands(self, log_dir: str = "."): ) def is_instantiated(self): + """ + Returns `True` if the node has been instantiated. + """ fablib_data = self.get_fablib_data() if "instantiated" not in fablib_data: logging.debug( @@ -2996,11 +3065,17 @@ def is_instantiated(self): return False def set_instantiated(self, instantiated: bool = True): + """ + Mark node as instantiated. Called by :py:meth:`config()`. + """ fablib_data = self.get_fablib_data() fablib_data["instantiated"] = str(instantiated) self.set_fablib_data(fablib_data) def run_update_commands(self): + """ + Returns `True` if `run_update_commands` flag is set. + """ fablib_data = self.get_fablib_data() if fablib_data["run_update_commands"] == "True": return True @@ -3008,6 +3083,9 @@ def run_update_commands(self): return False def set_run_update_commands(self, run_update_commands: bool = True): + """ + Set `run_update_commands` flag. + """ fablib_data = self.get_fablib_data() fablib_data["run_update_commands"] = str(run_update_commands) self.set_fablib_data(fablib_data) diff --git a/fabrictestbed_extensions/fablib/slice.py b/fabrictestbed_extensions/fablib/slice.py index 42134c9c..907bb049 100644 --- a/fabrictestbed_extensions/fablib/slice.py +++ b/fabrictestbed_extensions/fablib/slice.py @@ -923,36 +923,51 @@ def add_l2network( L2 networks types include: - - L2Bridge: a local Ethernet on a single site with unlimited interfaces. - - L2STS: a wide-area Ethernet on exactly two sites with unlimited interfaces. - Includes best effort performance and cannot, yet, support Basic NICs - residing on a single physical. - - L2PTP: a wide-area Ethernet on exactly two sites with exactly two interfaces. - QoS performance guarantees (coming soon!). Does not support Basic NICs. - Traffic arrives with VLAN tag and requires the node OS to configure - a VLAN interface. - - If the type argument is not set, FABlib will automatically choose the - L2 network type for you. In most cases the automatic network type is - the one you want. You can force a specific network type by setting the - type parameter to "L2Bridge", "L2STS", or "L2PTP". - - An exception will be raised if the set interfaces is not compatible - with the specified network type or if there is not compatible network - type for the given interface list. + - L2Bridge: a local Ethernet on a single site with + unlimited interfaces. + + - L2STS: a wide-area Ethernet on exactly two sites with + unlimited interfaces. Includes best effort performance + and cannot, yet, support Basic NICs residing on a single + physical. + + - L2PTP: a wide-area Ethernet on exactly two sites with + exactly two interfaces. QoS performance guarantees + (coming soon!). Does not support Basic NICs. Traffic + arrives with VLAN tag and requires the node OS to + configure a VLAN interface. + + If the type argument is not set, FABlib will automatically + choose the L2 network type for you. In most cases the + automatic network type is the one you want. You can force a + specific network type by setting the type parameter to + "L2Bridge", "L2STS", or "L2PTP". + + An exception will be raised if the set interfaces is not + compatible with the specified network type or if there is not + compatible network type for the given interface list. :param name: the name of the network service :type name: String - :param interfaces: a list of interfaces to build the network with + + :param interfaces: a list of interfaces to build the network + with :type interfaces: List[Interface] - :param type: optional L2 network type "L2Bridge", "L2STS", or "L2PTP" + + :param type: optional L2 network type "L2Bridge", "L2STS", or + "L2PTP" :type type: String + :param subnet: :type subnet: ipaddress + :param gateway: :type gateway: ipaddress + :param user_data + :type user_data: dict + :return: a new L2 network service :rtype: NetworkService """ @@ -981,40 +996,48 @@ def add_l3network( L3 networks types include: - - IPv4: An IPv4 network on the FABNetv4 internet - - IPv6: An IPv6 network on the FABNetv6 internet + - IPv4: An IPv4 network on the FABNetv4 internet + + - IPv6: An IPv6 network on the FABNetv6 internet The FABNet networks are internal IP internets that span the - FABRIC testbed. Adding a new L3 network to your FABRIC slice creates - an isolated network at a single site. FABRIC issues each isolated - L3 network with an IP subnet (either IPv4 or IPv6) and a gateway used - to route traffic to the FABNet internet. - - Like the public Internet, all FABNet networks can send traffic to all - other FABnet networks of the same type. In other words, FABNet networks - can be used to communicate between your slices and slices owned by - other users. - - An exception will be raised if the set interfaces is not from a single - FABRIC site. If you want to use L3 networks to connect slices that - are distributed across many site, you need to create a separate L3 - network for each site. - - It is important to note that by all nodes come with a default gateway - on a management network that use used to access your nodes (i.e. to - accept ssh connections). To use an L3 dataplane network, you will need - to add routes to your nodes that selectively route traffic across the - new dataplane network. You must be careful to maintain the default - gateway settings if you want to be able to access the node using the + FABRIC testbed. Adding a new L3 network to your FABRIC slice + creates an isolated network at a single site. FABRIC issues + each isolated L3 network with an IP subnet (either IPv4 or + IPv6) and a gateway used to route traffic to the FABNet + internet. + + Like the public Internet, all FABNet networks can send traffic + to all other FABnet networks of the same type. In other + words, FABNet networks can be used to communicate between your + slices and slices owned by other users. + + An exception will be raised if the set interfaces is not from + a single FABRIC site. If you want to use L3 networks to + connect slices that are distributed across many site, you need + to create a separate L3 network for each site. + + It is important to note that by all nodes come with a default + gateway on a management network that use used to access your + nodes (i.e. to accept ssh connections). To use an L3 + dataplane network, you will need to add routes to your nodes + that selectively route traffic across the new dataplane + network. You must be careful to maintain the default gateway + settings if you want to be able to access the node using the management network. :param name: the name of the network service :type name: String - :param interfaces: a list of interfaces to build the network with + + :param interfaces: a list of interfaces to build the network + with :type interfaces: List[Interface] + :param type: L3 network type "IPv4" or "IPv6" :type type: String + :param user_data + :type user_data: dict :return: a new L3 network service