Loading lib/chutney/TorNet.py +23 −71 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ def getenv_type(env_var, default, type_, type_name=None): """ Return the value of the environment variable 'envar' as type_, or 'default' if no such variable exists. Raise ValueError using type_name if the environment variable is set, but type_() raises a ValueError on its value. (If type_name is None or empty, the ValueError uses type_'s string representation instead.) Loading @@ -73,7 +72,6 @@ def getenv_int(env_var, default): """ Return the value of the environment variable 'envar' as an int, or 'default' if no such variable exists. Raise ValueError if the environment variable is set, but is not an int. """ return getenv_type(env_var, default, int, type_name='an int') Loading @@ -82,9 +80,7 @@ def getenv_bool(env_var, default): """ Return the value of the environment variable 'envar' as a bool, or 'default' if no such variable exists. Unlike bool(), converts 0, "False", and "No" to False. Raise ValueError if the environment variable is set, but is not a bool. """ try: Loading @@ -101,11 +97,9 @@ def getenv_bool(env_var, default): def mkdir_p(d, mode=448): """Create directory 'd' and all of its parents as needed. Unlike os.makedirs, does not give an error if d already exists. 448 is the decimal representation of the octal number 0700. Since python2 only supports 0700 and python3 only supports 0o700, we can use neither. Note that python2 and python3 differ in how they create the permissions for the intermediate directories. In python3, 'mode' only sets the mode for the last directory created. Loading Loading @@ -141,15 +135,12 @@ def get_absolute_net_path(): """ Returns the absolute path of the "net" directory that chutney should use to store "node*" directories containing torrcs and tor runtime data. If the CHUTNEY_DATA_DIR environmental variable is an absolute path, it is returned unmodified, regardless of whether the path actually exists. (Chutney creates any directories that do not exist.) Otherwise, if it is a relative path, and there is an existing directory with that name in the directory containing the chutney executable script, return that path (this check exists for legacy reasons). Finally, return the path relative to the current working directory, regardless of whether the path actually exists. """ Loading Loading @@ -179,8 +170,6 @@ def get_absolute_nodes_path(): This path is also used as a prefix for the unique nodes directory names. See get_new_absolute_nodes_path() for more details. """ return os.path.join(get_absolute_net_path(), 'nodes') Loading @@ -188,10 +177,8 @@ def get_new_absolute_nodes_path(now=time.time()): """ Returns the absolute path of a unique "nodes*" directory that chutney should use to store the current network's torrcs and tor runtime data. The nodes directory suffix is based on the current timestamp, incremented if necessary to avoid collisions with existing directories. (The existing directory check contains known race conditions: running multiple simultaneous chutney instances on the same "net" directory is not supported. The uniqueness check is only designed to avoid Loading Loading @@ -233,9 +220,7 @@ def _warnMissingTor(tor_path, cmdline, tor_name="tor"): def run_tor(cmdline, exit_on_missing=True): """Run the tor command line cmdline, which must start with the path or name of a tor binary. Returns the combined stdout and stderr of the process. If exit_on_missing is true, warn and exit if the tor binary is missing. Otherwise, raise a MissingBinaryException. """ Loading Loading @@ -272,7 +257,6 @@ def launch_process(cmdline, tor_name="tor", stdin=None, exit_on_missing=True): """Launch the command line cmdline, which must start with the path or name of a binary. Use tor_name as the canonical name of the binary in logs. Pass stdin to the Popen constructor. Returns the Popen object for the launched process. """ if tor_name == "tor": Loading Loading @@ -306,7 +290,6 @@ def run_tor_gencert(cmdline, passphrase): """Run the tor-gencert command line cmdline, which must start with the path or name of a tor-gencert binary. Then send passphrase to the stdin of the process. Returns the combined stdout and stderr of the process. """ p = launch_process(cmdline, Loading Loading @@ -377,7 +360,6 @@ def get_tor_modules(tor): """Check the list of compile-time modules advertised by the given 'tor' binary, and return a map from module name to a boolean describing whether it is supported. Unlisted modules are ones that Tor did not treat as compile-time optional modules. """ Loading Loading @@ -411,7 +393,6 @@ class Node(object): """A Node represents a Tor node or a set of Tor nodes. It's created in a network configuration file. This class is responsible for holding the user's selected node configuration, and figuring out how the node needs to be configured and launched. Loading Loading @@ -722,7 +703,6 @@ class LocalNodeBuilder(NodeBuilder): def _makeHiddenServiceDir(self): """Create the hidden service subdirectory for this node. The directory name is stored under the 'hs_directory' environment key. It is combined with the 'dir' data directory key to yield the path to the hidden service directory. Loading Loading @@ -907,6 +887,13 @@ class LocalNodeController(NodeController): except KeyError: return 0 def getEd25519Id(self): """Return the value of ed25519 key""" try: return self._env['ed25519_id'] except KeyError: return None def getBridgeClient(self): """Return the bridge client flag for this node.""" try: Loading Loading @@ -967,14 +954,13 @@ class LocalNodeController(NodeController): MIN_TOR_VERSION_FOR_MICRODESC_FIX = 'Tor 0.4' MIN_TIME_FOR_COMPLETE_CONSENSUS = V3_AUTH_VOTING_INTERVAL*1.5 MIN_START_TIME_LEGACY = MIN_TIME_FOR_COMPLETE_CONSENSUS + 20 MIN_START_TIME_LEGACY = 0 MIN_START_TIME_RECENT = 0 def getMinStartTime(self): """Returns the minimum start time before verifying, regardless of whether the network has bootstrapped, or the dir info has been distributed. Based on $CHUTNEY_EXTRA_START_TIME and the tor version. """ # User overrode the dynamic time Loading @@ -993,13 +979,12 @@ class LocalNodeController(NodeController): else: return LocalNodeController.MIN_START_TIME_RECENT NODE_WAIT_FOR_UNCHECKED_DIR_INFO = 10 NODE_WAIT_FOR_UNCHECKED_DIR_INFO = 0 HS_WAIT_FOR_UNCHECKED_DIR_INFO = V3_AUTH_VOTING_INTERVAL + 10 def getUncheckedDirInfoWaitTime(self): """Returns the amount of time to wait before verifying, after the network has bootstrapped, and the dir info has been distributed. Based on whether this node is an onion service. """ if self.getOnionService(): Loading Loading @@ -1255,17 +1240,14 @@ class LocalNodeController(NodeController): * a boolean indicating whether this node is a bridge client, and * a dict with the expected paths to the consensus files for this node. If v2_dir_paths is True, returns the v3 directory paths. Otherwise, returns the bridge status path. If v2_dir_paths is True, but this node is not a bridge client or bridge authority, returns None. (There are no paths.) Directory servers usually have both consensus flavours. Clients usually have the microdesc consensus, but they may have either flavour. (Or both flavours.) Only the bridge authority has the bridge networkstatus. The dict keys are: * "ns_cons", "desc", and "desc_new"; * "md_cons", "md", and "md_new"; and Loading Loading @@ -1318,9 +1300,7 @@ class LocalNodeController(NodeController): def getNodePublishedDirInfoPaths(self): """Return a dict of paths to consensus files, where we expect this node to be published. The dict keys are the nicks for each node. See getNodeCacheDirInfoPaths() for the path data structure, and which nodes appear in each type of directory. """ Loading Loading @@ -1348,14 +1328,10 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatusPattern(self, dir_format): """Returns a regular expression pattern for finding this node's entry in a dir_format file. The microdesc format is not yet implemented, because it requires the ed25519 key. (Or RSA block matching, which is hard.) in a dir_format file and returning None if nickname or ed25519_id key not found. """ nickname = self.getNick() ed25519_id = self._setEd25519Id() ed25519_key = self.getEd25519Id() cons = dir_format in ["ns_cons", "md_cons", Loading @@ -1379,17 +1355,17 @@ class LocalNodeController(NodeController): elif desc: return r'^router ' + nickname + " " elif md: # Not yet implemented, see #33428 return r'^id ed25519 ' + re.escape('ed25519_id') # r'^id ed25519 " + ed25519_identity (end of line) # needs ed25519-identity from #30642 # or the existing keys/ed25519_master_id_public_key #return None print(nickname) print(ed25519_key) if ed25519_key: return r'^id ed25519 ' + re.escape(ed25519_key) else: # If there is no ed25519_id, then we can't search for it return None def getFileDirInfoStatus(self, dir_format, dir_path): """Check dir_path, a directory path used by another node, to see if this node is present. The directory path is a dir_format file. Returns a status 3-tuple containing: * an integer status code: * negative numbers correspond to errors, Loading Loading @@ -1435,15 +1411,11 @@ class LocalNodeController(NodeController): """Combine the directory statuses in dir_status, if their keys appear in status_key_list. Keys may be directory formats, or node nicks. If best is True, choose the best status, otherwise, choose the worst status. If ignore_missing is True, ignore missing statuses, if there is any other status available. If statuses are equal, combine their format sets. Returns None if the status list is empty. """ dir_status_list = [ dir_status[status_key] Loading Loading @@ -1492,24 +1464,19 @@ class LocalNodeController(NodeController): to_bridge_client): """Summarise the statuses for this node, among all the files used by the other node. to_dir_server is True if the other node is a directory server. to_bridge_client is True if the other node is a bridge client. Combine these alternate files by choosing the best status: * desc_alts: "desc" and "desc_new" * md_alts: "md" and "md_new" Handle these alternate formats by ignoring missing directory files, then choosing the worst status: * cons_all: "ns_cons" and "md_cons" * desc_all: "desc"/"desc_new" and "md"/"md_new" Add an "node_dir" status that describes the overall status, which is the worst status among descriptors, consensuses, and the bridge networkstatus (if relevant). Return this status. Returns None if no status is expected. """ from_bridge = self.getBridge() Loading Loading @@ -1608,13 +1575,10 @@ class LocalNodeController(NodeController): to_bridge_client): """Check all the directory paths used by another node, to see if this node is present. to_dir_server is True if the other node is a directory server. to_bridge_client is True if the other node is a bridge client. Returns a dict containing a status 3-tuple for every relevant directory format. See getFileDirInfoStatus() for more details. Returns None if the node doesn't have any directory files containing published information from this node. """ Loading Loading @@ -1643,7 +1607,6 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatusList(self): """Look through the directories on each node, and work out if this node is in that directory. Returns a dict containing a status 3-tuple for each relevant node. The 3-tuple contains: * a status code, Loading @@ -1651,14 +1614,11 @@ class LocalNodeController(NodeController): * a status message string. See getNodeCacheDirInfoStatus() and getFileDirInfoStatus() for more details. If this node is a directory authority, bridge authority, or relay (including exits), checks v3 directory consensuses, descriptors, microdesc consensuses, and microdescriptors. If this node is a bridge, checks bridge networkstatuses, and descriptors on bridge authorities and bridge clients. If this node is a client (including onion services), returns None. """ dir_files = self.getNodePublishedDirInfoPaths() Loading Loading @@ -1696,7 +1656,6 @@ class LocalNodeController(NodeController): def summariseNodeDirInfoStatus(self, dir_status): """Summarise the statuses for this node's descriptor, among all the directory files used by all other nodes. Returns a dict containing a status 4-tuple for each status code. The 4-tuple contains: * a status code, Loading @@ -1706,11 +1665,9 @@ class LocalNodeController(NodeController): * a status message string. See getNodeCacheDirInfoStatus() and getFileDirInfoStatus() for more details. Also add an "node_all" status that describes the overall status, which is the worst status among all the other nodes' directory files. Returns None if no status is expected. """ node_status = dict() Loading Loading @@ -1777,7 +1734,6 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatus(self): """Return a 4-tuple describing the status of this node's descriptor, in all the directory documents across the network. If this node does not have a descriptor, returns None. """ dir_status = self.getNodeDirInfoStatusList() Loading @@ -1799,7 +1755,6 @@ class LocalNodeController(NodeController): def isInExpectedDirInfoDocs(self): """Return True if the descriptors for this node are in all expected directory documents. Return None if this node does not publish descriptors. """ node_status = self.getNodeDirInfoStatus() Loading Loading @@ -1892,9 +1847,7 @@ class TorEnviron(chutney.Templating.Environ): """Subclass of chutney.Templating.Environ to implement commonly-used substitutions. Environment fields provided: orport, controlport, socksport, dirport: *Port torrc option dir: DataDirectory torrc option nick: Nickname torrc option Loading @@ -1913,7 +1866,6 @@ class TorEnviron(chutney.Templating.Environ): Chutney users can disable the sandbox using: export CHUTNEY_TOR_SANDBOX=0 if it doesn't work on their version of glibc. Environment fields used: nodenum: chutney's internal node number for the node tag: a short text string that represents the type of node Loading Loading
lib/chutney/TorNet.py +23 −71 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ def getenv_type(env_var, default, type_, type_name=None): """ Return the value of the environment variable 'envar' as type_, or 'default' if no such variable exists. Raise ValueError using type_name if the environment variable is set, but type_() raises a ValueError on its value. (If type_name is None or empty, the ValueError uses type_'s string representation instead.) Loading @@ -73,7 +72,6 @@ def getenv_int(env_var, default): """ Return the value of the environment variable 'envar' as an int, or 'default' if no such variable exists. Raise ValueError if the environment variable is set, but is not an int. """ return getenv_type(env_var, default, int, type_name='an int') Loading @@ -82,9 +80,7 @@ def getenv_bool(env_var, default): """ Return the value of the environment variable 'envar' as a bool, or 'default' if no such variable exists. Unlike bool(), converts 0, "False", and "No" to False. Raise ValueError if the environment variable is set, but is not a bool. """ try: Loading @@ -101,11 +97,9 @@ def getenv_bool(env_var, default): def mkdir_p(d, mode=448): """Create directory 'd' and all of its parents as needed. Unlike os.makedirs, does not give an error if d already exists. 448 is the decimal representation of the octal number 0700. Since python2 only supports 0700 and python3 only supports 0o700, we can use neither. Note that python2 and python3 differ in how they create the permissions for the intermediate directories. In python3, 'mode' only sets the mode for the last directory created. Loading Loading @@ -141,15 +135,12 @@ def get_absolute_net_path(): """ Returns the absolute path of the "net" directory that chutney should use to store "node*" directories containing torrcs and tor runtime data. If the CHUTNEY_DATA_DIR environmental variable is an absolute path, it is returned unmodified, regardless of whether the path actually exists. (Chutney creates any directories that do not exist.) Otherwise, if it is a relative path, and there is an existing directory with that name in the directory containing the chutney executable script, return that path (this check exists for legacy reasons). Finally, return the path relative to the current working directory, regardless of whether the path actually exists. """ Loading Loading @@ -179,8 +170,6 @@ def get_absolute_nodes_path(): This path is also used as a prefix for the unique nodes directory names. See get_new_absolute_nodes_path() for more details. """ return os.path.join(get_absolute_net_path(), 'nodes') Loading @@ -188,10 +177,8 @@ def get_new_absolute_nodes_path(now=time.time()): """ Returns the absolute path of a unique "nodes*" directory that chutney should use to store the current network's torrcs and tor runtime data. The nodes directory suffix is based on the current timestamp, incremented if necessary to avoid collisions with existing directories. (The existing directory check contains known race conditions: running multiple simultaneous chutney instances on the same "net" directory is not supported. The uniqueness check is only designed to avoid Loading Loading @@ -233,9 +220,7 @@ def _warnMissingTor(tor_path, cmdline, tor_name="tor"): def run_tor(cmdline, exit_on_missing=True): """Run the tor command line cmdline, which must start with the path or name of a tor binary. Returns the combined stdout and stderr of the process. If exit_on_missing is true, warn and exit if the tor binary is missing. Otherwise, raise a MissingBinaryException. """ Loading Loading @@ -272,7 +257,6 @@ def launch_process(cmdline, tor_name="tor", stdin=None, exit_on_missing=True): """Launch the command line cmdline, which must start with the path or name of a binary. Use tor_name as the canonical name of the binary in logs. Pass stdin to the Popen constructor. Returns the Popen object for the launched process. """ if tor_name == "tor": Loading Loading @@ -306,7 +290,6 @@ def run_tor_gencert(cmdline, passphrase): """Run the tor-gencert command line cmdline, which must start with the path or name of a tor-gencert binary. Then send passphrase to the stdin of the process. Returns the combined stdout and stderr of the process. """ p = launch_process(cmdline, Loading Loading @@ -377,7 +360,6 @@ def get_tor_modules(tor): """Check the list of compile-time modules advertised by the given 'tor' binary, and return a map from module name to a boolean describing whether it is supported. Unlisted modules are ones that Tor did not treat as compile-time optional modules. """ Loading Loading @@ -411,7 +393,6 @@ class Node(object): """A Node represents a Tor node or a set of Tor nodes. It's created in a network configuration file. This class is responsible for holding the user's selected node configuration, and figuring out how the node needs to be configured and launched. Loading Loading @@ -722,7 +703,6 @@ class LocalNodeBuilder(NodeBuilder): def _makeHiddenServiceDir(self): """Create the hidden service subdirectory for this node. The directory name is stored under the 'hs_directory' environment key. It is combined with the 'dir' data directory key to yield the path to the hidden service directory. Loading Loading @@ -907,6 +887,13 @@ class LocalNodeController(NodeController): except KeyError: return 0 def getEd25519Id(self): """Return the value of ed25519 key""" try: return self._env['ed25519_id'] except KeyError: return None def getBridgeClient(self): """Return the bridge client flag for this node.""" try: Loading Loading @@ -967,14 +954,13 @@ class LocalNodeController(NodeController): MIN_TOR_VERSION_FOR_MICRODESC_FIX = 'Tor 0.4' MIN_TIME_FOR_COMPLETE_CONSENSUS = V3_AUTH_VOTING_INTERVAL*1.5 MIN_START_TIME_LEGACY = MIN_TIME_FOR_COMPLETE_CONSENSUS + 20 MIN_START_TIME_LEGACY = 0 MIN_START_TIME_RECENT = 0 def getMinStartTime(self): """Returns the minimum start time before verifying, regardless of whether the network has bootstrapped, or the dir info has been distributed. Based on $CHUTNEY_EXTRA_START_TIME and the tor version. """ # User overrode the dynamic time Loading @@ -993,13 +979,12 @@ class LocalNodeController(NodeController): else: return LocalNodeController.MIN_START_TIME_RECENT NODE_WAIT_FOR_UNCHECKED_DIR_INFO = 10 NODE_WAIT_FOR_UNCHECKED_DIR_INFO = 0 HS_WAIT_FOR_UNCHECKED_DIR_INFO = V3_AUTH_VOTING_INTERVAL + 10 def getUncheckedDirInfoWaitTime(self): """Returns the amount of time to wait before verifying, after the network has bootstrapped, and the dir info has been distributed. Based on whether this node is an onion service. """ if self.getOnionService(): Loading Loading @@ -1255,17 +1240,14 @@ class LocalNodeController(NodeController): * a boolean indicating whether this node is a bridge client, and * a dict with the expected paths to the consensus files for this node. If v2_dir_paths is True, returns the v3 directory paths. Otherwise, returns the bridge status path. If v2_dir_paths is True, but this node is not a bridge client or bridge authority, returns None. (There are no paths.) Directory servers usually have both consensus flavours. Clients usually have the microdesc consensus, but they may have either flavour. (Or both flavours.) Only the bridge authority has the bridge networkstatus. The dict keys are: * "ns_cons", "desc", and "desc_new"; * "md_cons", "md", and "md_new"; and Loading Loading @@ -1318,9 +1300,7 @@ class LocalNodeController(NodeController): def getNodePublishedDirInfoPaths(self): """Return a dict of paths to consensus files, where we expect this node to be published. The dict keys are the nicks for each node. See getNodeCacheDirInfoPaths() for the path data structure, and which nodes appear in each type of directory. """ Loading Loading @@ -1348,14 +1328,10 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatusPattern(self, dir_format): """Returns a regular expression pattern for finding this node's entry in a dir_format file. The microdesc format is not yet implemented, because it requires the ed25519 key. (Or RSA block matching, which is hard.) in a dir_format file and returning None if nickname or ed25519_id key not found. """ nickname = self.getNick() ed25519_id = self._setEd25519Id() ed25519_key = self.getEd25519Id() cons = dir_format in ["ns_cons", "md_cons", Loading @@ -1379,17 +1355,17 @@ class LocalNodeController(NodeController): elif desc: return r'^router ' + nickname + " " elif md: # Not yet implemented, see #33428 return r'^id ed25519 ' + re.escape('ed25519_id') # r'^id ed25519 " + ed25519_identity (end of line) # needs ed25519-identity from #30642 # or the existing keys/ed25519_master_id_public_key #return None print(nickname) print(ed25519_key) if ed25519_key: return r'^id ed25519 ' + re.escape(ed25519_key) else: # If there is no ed25519_id, then we can't search for it return None def getFileDirInfoStatus(self, dir_format, dir_path): """Check dir_path, a directory path used by another node, to see if this node is present. The directory path is a dir_format file. Returns a status 3-tuple containing: * an integer status code: * negative numbers correspond to errors, Loading Loading @@ -1435,15 +1411,11 @@ class LocalNodeController(NodeController): """Combine the directory statuses in dir_status, if their keys appear in status_key_list. Keys may be directory formats, or node nicks. If best is True, choose the best status, otherwise, choose the worst status. If ignore_missing is True, ignore missing statuses, if there is any other status available. If statuses are equal, combine their format sets. Returns None if the status list is empty. """ dir_status_list = [ dir_status[status_key] Loading Loading @@ -1492,24 +1464,19 @@ class LocalNodeController(NodeController): to_bridge_client): """Summarise the statuses for this node, among all the files used by the other node. to_dir_server is True if the other node is a directory server. to_bridge_client is True if the other node is a bridge client. Combine these alternate files by choosing the best status: * desc_alts: "desc" and "desc_new" * md_alts: "md" and "md_new" Handle these alternate formats by ignoring missing directory files, then choosing the worst status: * cons_all: "ns_cons" and "md_cons" * desc_all: "desc"/"desc_new" and "md"/"md_new" Add an "node_dir" status that describes the overall status, which is the worst status among descriptors, consensuses, and the bridge networkstatus (if relevant). Return this status. Returns None if no status is expected. """ from_bridge = self.getBridge() Loading Loading @@ -1608,13 +1575,10 @@ class LocalNodeController(NodeController): to_bridge_client): """Check all the directory paths used by another node, to see if this node is present. to_dir_server is True if the other node is a directory server. to_bridge_client is True if the other node is a bridge client. Returns a dict containing a status 3-tuple for every relevant directory format. See getFileDirInfoStatus() for more details. Returns None if the node doesn't have any directory files containing published information from this node. """ Loading Loading @@ -1643,7 +1607,6 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatusList(self): """Look through the directories on each node, and work out if this node is in that directory. Returns a dict containing a status 3-tuple for each relevant node. The 3-tuple contains: * a status code, Loading @@ -1651,14 +1614,11 @@ class LocalNodeController(NodeController): * a status message string. See getNodeCacheDirInfoStatus() and getFileDirInfoStatus() for more details. If this node is a directory authority, bridge authority, or relay (including exits), checks v3 directory consensuses, descriptors, microdesc consensuses, and microdescriptors. If this node is a bridge, checks bridge networkstatuses, and descriptors on bridge authorities and bridge clients. If this node is a client (including onion services), returns None. """ dir_files = self.getNodePublishedDirInfoPaths() Loading Loading @@ -1696,7 +1656,6 @@ class LocalNodeController(NodeController): def summariseNodeDirInfoStatus(self, dir_status): """Summarise the statuses for this node's descriptor, among all the directory files used by all other nodes. Returns a dict containing a status 4-tuple for each status code. The 4-tuple contains: * a status code, Loading @@ -1706,11 +1665,9 @@ class LocalNodeController(NodeController): * a status message string. See getNodeCacheDirInfoStatus() and getFileDirInfoStatus() for more details. Also add an "node_all" status that describes the overall status, which is the worst status among all the other nodes' directory files. Returns None if no status is expected. """ node_status = dict() Loading Loading @@ -1777,7 +1734,6 @@ class LocalNodeController(NodeController): def getNodeDirInfoStatus(self): """Return a 4-tuple describing the status of this node's descriptor, in all the directory documents across the network. If this node does not have a descriptor, returns None. """ dir_status = self.getNodeDirInfoStatusList() Loading @@ -1799,7 +1755,6 @@ class LocalNodeController(NodeController): def isInExpectedDirInfoDocs(self): """Return True if the descriptors for this node are in all expected directory documents. Return None if this node does not publish descriptors. """ node_status = self.getNodeDirInfoStatus() Loading Loading @@ -1892,9 +1847,7 @@ class TorEnviron(chutney.Templating.Environ): """Subclass of chutney.Templating.Environ to implement commonly-used substitutions. Environment fields provided: orport, controlport, socksport, dirport: *Port torrc option dir: DataDirectory torrc option nick: Nickname torrc option Loading @@ -1913,7 +1866,6 @@ class TorEnviron(chutney.Templating.Environ): Chutney users can disable the sandbox using: export CHUTNEY_TOR_SANDBOX=0 if it doesn't work on their version of glibc. Environment fields used: nodenum: chutney's internal node number for the node tag: a short text string that represents the type of node Loading