1
2
3
4
5
6
7
8
9
10
11 import copy
12 import re
13 import threading
14
15 import rospy
16
17
18 import utils
19 from gateway_msgs.msg import Rule
20
21
22
23
24
25
27 '''
28 Checks that the public rule doesn't already exist in the list of public
29 rules (which can represent the public interface or the rules themselves).
30 We only need to compare the name/node as they uniquely identify the
31 name/regex
32
33 @param public_rule : the rule to search for
34 @type Rule
35
36 @param public_rules : list of Rule (public, watchlist or blacklist)
37 @type list : list of Rule objects
38
39 @return True if the public rule exists, False otherwise
40 @rtype bool
41 '''
42 for rule in public_rules:
43 if rule.name == public_rule.name and \
44 rule.node == public_rule.node:
45 return True
46 return False
47
48
49
50
51
52
54 '''
55 The public interface is the set of rules
56 (pubs/subs/services/actions) that are exposed and made available for
57 freely sharing with a multimaster system.
58
59 It consists of:
60 * list of currently available rules to be shared
61 * list of rules and filters that will be watched
62 and shared if they become available
63 '''
64 - def __init__(self, default_rule_blacklist, default_rules):
65 '''
66 Initialises the public interface
67
68 @param default_rule_blacklist : connection type keyed dictionary of rules
69 @type str keyed dictionary of gateway_msgs.msg.Rule[]
70
71 @param default_rules : connection type keyed dictionary of rules
72 @type str keyed dictionary of gateway_msgs.msg.Rule[]
73 '''
74
75
76 self.watchlist = utils.create_empty_connection_type_dictionary()
77
78
79 self._default_blacklist = default_rule_blacklist
80
81
82 self.blacklist = self._default_blacklist
83
84
85 self.public = utils.create_empty_connection_type_dictionary()
86
87 self.advertise_all_enabled = False
88
89 self.lock = threading.Lock()
90
91
92 for connection_type in utils.connection_types:
93 for rule in default_rules[connection_type]:
94 self.add_rule(rule)
95
96
97
98
99
101 '''
102 Watch for a new public rule, as described for by the incoming message.
103
104 @param rule : a rule msg from the advertise call
105 @type Rule
106
107 @return the rule if added, or None if the rule exists already
108 @rtype Rule || None
109 '''
110 result = None
111 self.lock.acquire()
112 if not publicRuleExists(rule, self.watchlist[rule.type]):
113 self.watchlist[rule.type].append(rule)
114 result = rule
115 self.lock.release()
116 rospy.loginfo("Gateway : adding rule to public watchlist %s" % utils.format_rule(rule))
117 return result
118
120 '''
121 Attempt to remove a watchlist rule from the public interface. Be a
122 bit careful looking for a rule to remove, depending on the node name,
123 which can be set (exact rule/node name match) or None in which case all
124 nodes of that kind of advertisement will match.
125
126 @param rule : a rule to unadvertise
127 @type Rule
128
129 @return the list of rules removed
130 @rtype Rule[]
131 '''
132
133 rospy.loginfo("Gateway : (req) unadvertise %s" % utils.format_rule(rule))
134
135 if rule.node:
136
137 try:
138 self.lock.acquire()
139 self.watchlist[rule.type].remove(rule)
140 self.lock.release()
141 return [rule]
142 except ValueError:
143 self.lock.release()
144 return []
145 else:
146
147
148 existing_rules = []
149 self.lock.acquire()
150 for existing_rule in self.watchlist[rule.type]:
151 if (existing_rule.name == rule.name):
152 existing_rules.append(existing_rule)
153 for rule in existing_rules:
154 self.watchlist[rule.type].remove(existing_rule)
155 self.lock.release()
156 return existing_rules
157
159 '''
160 Allow all rules apart from the ones in the provided blacklist +
161 default blacklist
162
163 @param blacklist : list of Rule objects
164 @type list : list of Rule objects
165
166 @return failure if already advertising all, success otherwise
167 @rtype bool
168 '''
169 rospy.loginfo("Gateway : received a request advertise everything!")
170 self.lock.acquire()
171
172
173 if self.advertise_all_enabled:
174 self.lock.release()
175 return False
176 self.advertise_all_enabled = True
177
178
179 self.watchlist = utils.create_empty_connection_type_dictionary()
180 for connection_type in utils.connection_types:
181 allow_all_rule = Rule()
182 allow_all_rule.name = '.*'
183 allow_all_rule.type = connection_type
184 allow_all_rule.node = '.*'
185 self.watchlist[connection_type].append(allow_all_rule)
186
187
188 self.blacklist = copy.deepcopy(self._default_blacklist)
189 for rule in blacklist:
190 if not publicRuleExists(rule, self.blacklist[rule.type]):
191 self.blacklist[rule.type].append(rule)
192
193 self.lock.release()
194 return True
195
197 '''
198 Disallow the allow all mode, if enabled. If allow all mode is not
199 enabled, remove everything from the public interface
200 '''
201 rospy.loginfo("Gateway : received a request to remove all advertisements!")
202 self.lock.acquire()
203
204
205 self.advertise_all_enabled = False
206
207
208 self.watchlist = utils.create_empty_connection_type_dictionary()
209 self.blacklist = self._default_blacklist
210
211 self.lock.release()
212
213
214
215
216
218 l = []
219 self.lock.acquire()
220 for connection_type in utils.connection_types:
221 l.extend([connection.rule for connection in self.public[connection_type]])
222 self.lock.release()
223 return l
224
226 l = []
227 self.lock.acquire()
228 for connection_type in utils.connection_types:
229 l.extend(self.watchlist[connection_type])
230 self.lock.release()
231 return l
232
234 l = []
235 self.lock.acquire()
236 for connection_type in utils.connection_types:
237 l.extend(self.blacklist[connection_type])
238 self.lock.release()
239 return l
240
241
242
243
244
246 '''
247 Match a given rule/rule against a given rule list
248
249 @param rules : the rules against which to match
250 @type dict of list of Rule objects
251 @param rule : the given rule/rule to match
252 @type Rule
253 @return the list of rules matched, None if no rules found
254 @rtype list of Rules || None
255 '''
256 matched = False
257 for r in rules[rule.type]:
258 name_match_result = re.match(r.name, rule.name)
259 if name_match_result and name_match_result.group() == rule.name:
260 if r.node:
261 node_match_result = re.match(r.node, rule.node)
262 if node_match_result and node_match_result.group() == rule.node:
263 matched = True
264 else:
265 matched = True
266 if matched:
267 break
268 return matched
269
271 '''
272 Determines whether a given rule should be allowed given the
273 status of the current watchlist and blacklist
274
275 @param rule : the given rule/rule to match
276 @type Rule
277 @return whether rule is allowed
278 @rtype bool
279 '''
280 self.lock.acquire()
281 matched_rules = self._matchAgainstRuleList(self.watchlist, rule)
282 matched_blacklisted_rules = self._matchAgainstRuleList(self.blacklist, rule)
283 self.lock.release()
284 success = False
285 if matched_rules and not matched_blacklisted_rules:
286 success = True
287 return success
288
290 '''
291 Given a rule, determines if the rule is allowed. If it is
292 allowed, then returns the corresponding Rule object
293
294 @param rules : the given rules to match
295 @type Rule
296 @return The generated Rule if allowed, None if no match
297 @rtype Rule || None
298 '''
299 if self._allowRule(rule):
300 return Rule(rule)
301 return None
302
303 - def update(self, connections):
304 '''
305 Checks a list of rules and determines which ones should be
306 added/removed to the public interface. Modifies the public interface
307 accordingly, and returns the list of rules to the gateway for
308 hub operations
309
310 @param rules: the list of rules available locally
311 @type dict of lists of Rule objects
312
313 @return: new public connections, as well as connections to be removed
314 @rtype: Connection[], Connection[]
315 '''
316
317 public = utils.create_empty_connection_type_dictionary()
318 new_public = utils.create_empty_connection_type_dictionary()
319 removed_public = utils.create_empty_connection_type_dictionary()
320 diff = lambda l1,l2: [x for x in l1 if x not in l2]
321 for connection_type in connections:
322 for connection in connections[connection_type]:
323 if self._allowRule(connection.rule):
324 public[connection_type].append(connection)
325 new_public[connection_type] = diff(public[connection_type],self.public[connection_type])
326 removed_public[connection_type] = diff(self.public[connection_type],public[connection_type])
327
328 self.lock.acquire()
329 self.public = public
330 self.lock.release()
331
332 return new_public, removed_public
333