def _generate_concert_alias(self, gateway_name): ''' Generate a friendly concert alias for this client given the (usually) uuid suffixed gateway name. :param str gateway_name: the uuid'd gateway name (e.g. kobuki95fbd06982344cfc9b013ef7b184e420) :return: the concert alias :rtype: str ''' gateway_basename = rocon_gateway_utils.gateway_basename(gateway_name) # remove the 16 byte hex hash from the name same_name_count = 0 human_friendly_indices = set([]) for concert_client in self._flat_client_dict.values(): if gateway_basename == rocon_gateway_utils.gateway_basename( concert_client.gateway_name): index = concert_client.concert_alias.replace( gateway_basename, "") if index == "": human_friendly_indices.add("0") else: human_friendly_indices.add(index) same_name_count += 1 human_friendly_index = -1 while True: human_friendly_index += 1 if not str(human_friendly_index) in human_friendly_indices: break concert_name = gateway_basename if human_friendly_index == 0 else gateway_basename + str( human_friendly_index) return concert_name
def __init__(self, ip, port, whitelist=[], blacklist=[]): ''' @param remote_gateway_request_callbacks : to handle redis responses @type list of function pointers (back to GatewaySync class @param ip : redis server ip @param port : redis server port @raise HubNameNotFoundError, HubNotFoundError ''' # variables self.ip = ip self.port = port self.uri = str(ip) + ":" + str(port) self._redis_keys = {} self._redis_channels = {} # This is a temporary try-except block just to ping and see if the address we have here is # actually resolvable or it times out. Ideally we want to use socket_timeouts throughout, # but that will need modification of the way we handle the RedisListenerThread in # gateway_hub.py try: unused_ping = redis.Redis(host=ip, socket_timeout=0.5, port=port).ping() # should check ping result? Typically it just throws the timeout error except redis.exceptions.ConnectionError: self._redis_server = None raise HubNotFoundError("couldn't connect to the redis server") try: self.pool = redis.ConnectionPool(host=ip, port=port, db=0) self._redis_server = redis.Redis(connection_pool=self.pool) self._redis_pubsub_server = self._redis_server.pubsub() hub_key_name = self._redis_server.get("rocon:hub:name") # Be careful, hub_name is None, it means the redis server is # found but hub_name not yet set or not set at all. if not hub_key_name: self._redis_server = None raise HubNameNotFoundError("couldn't resolve hub name on the redis server [%s:%s]" % (ip, port)) else: self.name = hub_api.key_base_name(hub_key_name) # perhaps should store all key names somewhere central rospy.logdebug("Gateway : resolved hub name [%s].", self.name) except redis.exceptions.ConnectionError: self._redis_server = None raise HubNotFoundError("couldn't connect to the redis server") # whitelists, blacklists - check against uri's hash names and non-uuid names uri_blacklist = [urlparse(x).hostname + ':' + str(urlparse(x).port) for x in blacklist if urlparse(x).hostname is not None] uri_whitelist = [urlparse(x).hostname + ':' + str(urlparse(x).port) for x in whitelist if urlparse(x).hostname is not None] nonuuid_blacklist = [rocon_gateway_utils.gateway_basename(x) for x in blacklist if urlparse(x) is None and rocon_gateway_utils.gateway_basename(x)] nonuuid_whitelist = [rocon_gateway_utils.gateway_basename(x) for x in whitelist if urlparse(x) is None and rocon_gateway_utils.gateway_basename(x)] if self.uri in uri_blacklist or self.name in blacklist or self.name in nonuuid_blacklist: raise HubConnectionBlacklistedError("ignoring blacklisted hub [%s]" % self.uri) if self.name in blacklist or self.name in nonuuid_whitelist: raise HubConnectionBlacklistedError("ignoring blacklisted hub [%s]" % self.uri) if not ((len(whitelist) == 0) or (self.uri in uri_whitelist) or (self.name in whitelist)): raise HubConnectionNotWhitelistedError("hub/ip not in non-empty whitelist [%s, %s][%s]" % (self.name, self.uri, whitelist))
def wait_for_remote_gateway(remote_gateway_name, ns=_gateway_namespace, timeout=rospy.Duration(5.0)): ''' Slowly loop (and block) until remote the gateway is visible on our hub. ''' rospy.wait_for_service(ns + '/remote_gateway_info') remote_gateway_info_service = rospy.ServiceProxy( ns + '/remote_gateway_info', gateway_srvs.RemoteGatewayInfo) req = gateway_srvs.RemoteGatewayInfoRequest() start_time = rospy.Time.now() while not rospy.is_shutdown(): req = gateway_srvs.RemoteGatewayInfoRequest() req.gateways = [] resp = remote_gateway_info_service(req) matched = False if rospy.Time.now() - start_time > timeout: raise GatewaySampleRuntimeError( "timed out waiting for a remote gateway to appear") for gateway in resp.gateways: if remote_gateway_name == gateway.name: matched = True break remote_gateway_basename = rocon_gateway_utils.gateway_basename( gateway.name) print("Samples: gateway comparison [%s][%s]" % (remote_gateway_basename, remote_gateway_name)) if remote_gateway_name == remote_gateway_basename: matched = True break if matched: break
def test_graph(self): flips = None while not flips: printtest("Waiting for flips") self.graph.update() flips = self.graph._local_gateway.flip_watchlist rospy.rostime.wallsleep(0.2) printtest("********************************************************************") printtest("* Local Gateway") printtest("********************************************************************") printtest("%s" % self.graph._local_gateway) self.assertEquals("5", str(len(flips))) # TODO: this is currently returning the base name, is should be returning the hash name self.assertEquals(len([flip for flip in flips if flip.gateway == "remote_gateway" and flip.rule.name == "/add_two_ints" and flip.rule.type == "service"]), 1) self.assertEquals(len([flip for flip in flips if flip.gateway == "remote_gateway" and flip.rule.name == "/chatter" and flip.rule.type == "publisher"]), 1) self.assertEquals(len([flip for flip in flips if flip.gateway == "remote_gateway" and flip.rule.name == "/chatter" and flip.rule.type == "subscriber"]), 1) self.assertEquals(len([flip for flip in flips if flip.gateway == "remote_gateway" and flip.rule.name == "/fibonacci" and flip.rule.type == "action_server"]), 1) self.assertEquals(len([flip for flip in flips if flip.gateway == "remote_gateway" and flip.rule.name == "/fibonacci" and flip.rule.type == "action_client"]), 1) printtest("********************************************************************") printtest("* Remote Gateway") printtest("********************************************************************") printtest("%s" % self.graph._remote_gateways) for remote_gateway in self.graph._remote_gateways: self.assertEquals("remote_gateway", rocon_gateway_utils.gateway_basename(remote_gateway.name))
def _add_node(self, node, rosgraphinst, dotcode_factory, dotgraph): if node in rosgraphinst.bad_nodes: bn = rosgraphinst.bad_nodes[node] if bn.type == rosgraph.impl.graph.BadNode.DEAD: dotcode_factory.add_node_to_graph(dotgraph, nodename=node, shape="doublecircle", url=node, color="red") else: dotcode_factory.add_node_to_graph(dotgraph, nodename=node, shape="doublecircle", url=node, color="orange") else: dotcode_factory.add_node_to_graph( dotgraph, nodename=node, #nodename=rocon_gateway_utils.gateway_basename(node), #nodelabel=rocon_gateway_utils.gateway_basename(node), shape='ellipse', url=rocon_gateway_utils.gateway_basename(node), #url=node )
def test_graph(self): flips = None while not flips: printtest("Waiting for flips") self.graph.update() flips = self.graph._local_gateway.flip_watchlist rospy.rostime.wallsleep(0.2) printtest( "********************************************************************" ) printtest("* Local Gateway") printtest( "********************************************************************" ) printtest("%s" % self.graph._local_gateway) self.assertEquals("1", str(len(flips))) # TODO: this is currently returning the base name, is should be returning the hash name self.assertEquals("remote_gateway", flips[0].gateway) self.assertEquals("publisher", flips[0].rule.type) self.assertEquals("/chatter", flips[0].rule.name) printtest( "********************************************************************" ) printtest("* Remote Gateway") printtest( "********************************************************************" ) printtest("%s" % self.graph._remote_gateways) for remote_gateway in self.graph._remote_gateways: self.assertEquals( "remote_gateway", rocon_gateway_utils.gateway_basename(remote_gateway.name))
def _add_node(self, node, dotcode_factory, dotgraph): ''' A node here is a concert client. We basically add nodes and classify according to their state in the dotgraph. :param node .concert_client.ConcertClient: the concert client to show on the dotgraph ''' # colour strings defined as per http://qt-project.org/doc/qt-4.8/qcolor.html#setNamedColor # and http://www.w3.org/TR/SVG/types.html#ColorKeywords if node.state == concert_msgs.ConcertClientState.PENDING: node_colour = "magenta" elif node.state == concert_msgs.ConcertClientState.AVAILABLE: node_colour = "blue" elif node.state == concert_msgs.ConcertClientState.MISSING: node_colour = "powderblue" elif node.state == concert_msgs.ConcertClientState.GONE: node_colour = "black" elif node.state == concert_msgs.ConcertClientState.BAD: node_colour = "red" dotcode_factory.add_node_to_graph(dotgraph, nodename=node.concert_alias, # nodename=rocon_gateway_utils.gateway_basename(node), # nodelabel=rocon_gateway_utils.gateway_basename(node), shape='ellipse', url=rocon_gateway_utils.gateway_basename(node.gateway_name), # url=node, color=node_colour )
def _generate_pulls(self, connection_type, name, node, gateway, unique_name): ''' Checks if a local rule (obtained from master.get_system_state) is a suitable association with any of the rules or patterns. This can return multiple matches, since the same local rule properties can be multiply pulled to different remote gateways. Used in the update() call above that is run in the watcher thread. Note, don't need to lock here as the update() function takes care of it. @param connection_type : rule type @type str : string constant from gateway_msgs.Rule @param name : fully qualified topic, service or action name @type str @param node : ros node name (coming from master.get_system_state) @type str @param gateway : remote gateway hash name. @type str @return all the pull rules that match this local rule @return list of RemoteRule objects updated with node names from self.watchlist ''' matched_pull_rules = [] for rule in self.watchlist[connection_type]: # check for regular expression or perfect match gateway_match_result = re.match(rule.gateway, gateway) matched = False if gateway_match_result and gateway_match_result.group( ) == gateway: matched = True elif rule.gateway == rocon_gateway_utils.gateway_basename(gateway): matched = True if not matched: continue # Check names rule_name = rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if not matched: rule_name = '/' + unique_name + '/' + rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if not matched: rule_name = '/' + rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if matched: matched_pull = copy.deepcopy(rule) matched_pull.gateway = gateway # just in case we used a regex or matched basename matched_pull.rule.name = name # just in case we used a regex matched_pull.rule.node = node # just in case we used a regex matched_pull_rules.append(matched_pull) return matched_pull_rules
def _generate_pulls(self, connection_type, name, node, gateway, unique_name): ''' Checks if a local rule (obtained from master.get_system_state) is a suitable association with any of the rules or patterns. This can return multiple matches, since the same local rule properties can be multiply pulled to different remote gateways. Used in the update() call above that is run in the watcher thread. Note, don't need to lock here as the update() function takes care of it. @param connection_type : rule type @type str : string constant from gateway_msgs.Rule @param name : fully qualified topic, service or action name @type str @param node : ros node name (coming from master.get_system_state) @type str @param gateway : remote gateway hash name. @type str @return all the pull rules that match this local rule @return list of RemoteRule objects updated with node names from self.watchlist ''' matched_pull_rules = [] for rule in self.watchlist[connection_type]: # check for regular expression or perfect match gateway_match_result = re.match(rule.gateway, gateway) matched = False if gateway_match_result and gateway_match_result.group() == gateway: matched = True elif rule.gateway == rocon_gateway_utils.gateway_basename(gateway): matched = True if not matched: continue # Check names rule_name = rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if not matched: rule_name = '/' + unique_name + '/' + rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if not matched: rule_name = '/' + rule.rule.name matched = self.is_matched(rule, rule_name, name, node) if matched: matched_pull = copy.deepcopy(rule) matched_pull.gateway = gateway # just in case we used a regex or matched basename matched_pull.rule.name = name # just in case we used a regex matched_pull.rule.node = node # just in case we used a regex matched_pull_rules.append(matched_pull) return matched_pull_rules
def _get_matched_gateways(self, flip_rule, remote_gateways): matched_gateways = [] for gateway in remote_gateways: # check for regular expression or perfect match gateway_match_result = re.match(flip_rule.gateway, gateway) if gateway_match_result and gateway_match_result.group() == gateway: matched_gateways.append(gateway) elif flip_rule.gateway == rocon_gateway_utils.gateway_basename(gateway): matched_gateways.append(gateway) return matched_gateways
def matches_remote_gateway_basename(self, gateway): ''' Use this when gateway can be a regular expression and we need to check it off against list_remote_gateway_names() ''' weak_matches = [] try: for remote_gateway in self.list_remote_gateway_names(): if re.match(gateway, rocon_gateway_utils.gateway_basename(remote_gateway)): weak_matches.append(remote_gateway) except HubConnectionLostError: raise return weak_matches
def __init__(self, ip, port, whitelist=[], blacklist=[]): ''' @param remote_gateway_request_callbacks : to handle redis responses @type list of function pointers (back to GatewaySync class @param ip : redis server ip @param port : redis server port @raise HubNameNotFoundError, HubNotFoundError ''' # variables self.ip = ip self.port = port self.uri = str(ip) + ":" + str(port) self._redis_keys = {} self._redis_channels = {} # This is a temporary try-except block just to ping and see if the address we have here is # actually resolvable or it times out. Ideally we want to use socket_timeouts throughout, # but that will need modification of the way we handle the RedisListenerThread in # gateway_hub.py try: unused_ping = redis.Redis(host=ip, socket_timeout=5.0, port=port).ping() # should check ping result? Typically it just throws the timeout error except redis.exceptions.ConnectionError: self._redis_server = None raise HubNotFoundError("couldn't connect to the redis server") try: self.pool = redis.ConnectionPool(host=ip, port=port, db=0, socket_timeout=5.0) self._redis_server = redis.Redis(connection_pool=self.pool) self._redis_pubsub_server = self._redis_server.pubsub() hub_key_name = self._redis_server.get("rocon:hub:name") # Be careful, hub_name is None, it means the redis server is # found but hub_name not yet set or not set at all. # retrying for 5 seconds in case we started too fast retries = 0 while self._redis_server and not hub_key_name and retries < 5: rospy.logwarn("couldn't resolve hub name on the redis server [%s:%s]. Retrying..." % (ip, port)) retries += 1 rospy.rostime.wallsleep(1.0) hub_key_name = self._redis_server.get("rocon:hub:name") if not hub_key_name: self._redis_server = None raise HubNameNotFoundError("couldn't resolve hub name on the redis server [%s:%s]" % (ip, port)) else: self.name = hub_api.key_base_name(hub_key_name) # perhaps should store all key names somewhere central rospy.logdebug("Gateway : resolved hub name [%s].", self.name) except redis.exceptions.ConnectionError: self._redis_server = None raise HubNotFoundError("couldn't connect to the redis server") # whitelists, blacklists - check against uri's hash names and non-uuid names uri_blacklist = [urlparse(x).hostname + ':' + str(urlparse(x).port) for x in blacklist if urlparse(x).hostname is not None] uri_whitelist = [urlparse(x).hostname + ':' + str(urlparse(x).port) for x in whitelist if urlparse(x).hostname is not None] nonuuid_blacklist = [rocon_gateway_utils.gateway_basename(x) for x in blacklist if urlparse(x) is None and rocon_gateway_utils.gateway_basename(x)] nonuuid_whitelist = [rocon_gateway_utils.gateway_basename(x) for x in whitelist if urlparse(x) is None and rocon_gateway_utils.gateway_basename(x)] if self.uri in uri_blacklist or self.name in blacklist or self.name in nonuuid_blacklist: raise HubConnectionBlacklistedError("ignoring blacklisted hub [%s]" % self.uri) if self.name in blacklist or self.name in nonuuid_whitelist: raise HubConnectionBlacklistedError("ignoring blacklisted hub [%s]" % self.uri) if not ((len(whitelist) == 0) or (self.uri in uri_whitelist) or (self.name in whitelist)): raise HubConnectionNotWhitelistedError("hub/ip not in non-empty whitelist [%s, %s][%s]" % (self.name, self.uri, whitelist))
def _generate_flips(self, connection_type, name, node, remote_gateways, unique_name): ''' Checks if a local rule (obtained from master.get_system_state) is a suitable association with any of the rules or patterns. This can return multiple matches, since the same local rule properties can be multiply flipped to different remote gateways. Used in the update() call above that is run in the watcher thread. Note, don't need to lock here as the update() function takes care of it. @param connection_type : rule type @type str : string constant from gateway_msgs.msg.Rule @param name : fully qualified topic, service or action name @type str @param node : ros node name (coming from master.get_system_state) @type str @param gateways : gateways that are available (registered on the hub) @type string @return all the flip rules that match this local rule @return list of RemoteRule objects updated with node names from self.watchlist ''' matched_flip_rules = [] for flip_rule in self.watchlist[connection_type]: # Check if the flip rule corresponds to an existing gateway matched_gateways = [] for gateway in remote_gateways: # check for regular expression or perfect match gateway_match_result = re.match(flip_rule.gateway, gateway) if gateway_match_result and gateway_match_result.group() == gateway: matched_gateways.append(gateway) elif flip_rule.gateway == rocon_gateway_utils.gateway_basename(gateway): matched_gateways.append(gateway) if not matched_gateways: continue # Check names rule_name = flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if not utils.is_all_pattern(flip_rule.rule.name): if not matched: rule_name = '/' + unique_name + '/' + flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if not matched: rule_name = '/' + flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if matched: for gateway in matched_gateways: matched_flip = copy.deepcopy(flip_rule) matched_flip.gateway = gateway # just in case we used a regex or matched basename matched_flip.rule.name = name # just in case we used a regex matched_flip.rule.node = node # just in case we used a regex matched_flip_rules.append(matched_flip) return matched_flip_rules
def _generate_flips(self, connection_type, name, node, remote_gateways, unique_name): ''' Checks if a local rule (obtained from master.get_system_state) is a suitable association with any of the rules or patterns. This can return multiple matches, since the same local rule properties can be multiply flipped to different remote gateways. Used in the update() call above that is run in the watcher thread. Note, don't need to lock here as the update() function takes care of it. @param connection_type : rule type @type str : string constant from gateway_msgs.msg.Rule @param name : fully qualified topic, service or action name @type str @param node : ros node name (coming from master.get_system_state) @type str @param gateways : gateways that are available (registered on the hub) @type string @return all the flip rules that match this local rule @return list of RemoteRule objects updated with node names from self.watchlist ''' matched_flip_rules = [] for flip_rule in self.watchlist[connection_type]: # Check if the flip rule corresponds to an existing gateway matched_gateways = [] for gateway in remote_gateways: # check for regular expression or perfect match gateway_match_result = re.match(flip_rule.gateway, gateway) if gateway_match_result and gateway_match_result.group( ) == gateway: matched_gateways.append(gateway) elif flip_rule.gateway == rocon_gateway_utils.gateway_basename( gateway): matched_gateways.append(gateway) if not matched_gateways: continue # Check names rule_name = flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if not utils.is_all_pattern(flip_rule.rule.name): if not matched: rule_name = '/' + unique_name + '/' + flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if not matched: rule_name = '/' + flip_rule.rule.name matched = self.is_matched(flip_rule, rule_name, name, node) if matched: for gateway in matched_gateways: matched_flip = copy.deepcopy(flip_rule) matched_flip.gateway = gateway # just in case we used a regex or matched basename matched_flip.rule.name = name # just in case we used a regex matched_flip.rule.node = node # just in case we used a regex matched_flip_rules.append(matched_flip) return matched_flip_rules
def test_basename(self): self.assertEquals( "dude", rocon_gateway_utils.gateway_basename( 'dude1285014a28c74162bf19952d1481197e'))