Mercurial > libervia-backend
comparison sat_frontends/jp/cmd_pubsub.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | b2f323237fce |
children | fee60f17ebac |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 | 20 |
21 import base | 21 from . import base |
22 from sat.core.i18n import _ | 22 from sat.core.i18n import _ |
23 from sat.core import exceptions | 23 from sat.core import exceptions |
24 from sat_frontends.jp.constants import Const as C | 24 from sat_frontends.jp.constants import Const as C |
25 from sat_frontends.jp import common | 25 from sat_frontends.jp import common |
26 from sat_frontends.jp import arg_tools | 26 from sat_frontends.jp import arg_tools |
35 import subprocess | 35 import subprocess |
36 import sys | 36 import sys |
37 | 37 |
38 __commands__ = ["Pubsub"] | 38 __commands__ = ["Pubsub"] |
39 | 39 |
40 PUBSUB_TMP_DIR = u"pubsub" | 40 PUBSUB_TMP_DIR = "pubsub" |
41 PUBSUB_SCHEMA_TMP_DIR = PUBSUB_TMP_DIR + "_schema" | 41 PUBSUB_SCHEMA_TMP_DIR = PUBSUB_TMP_DIR + "_schema" |
42 ALLOWED_SUBSCRIPTIONS_OWNER = ("subscribed", "pending", "none") | 42 ALLOWED_SUBSCRIPTIONS_OWNER = ("subscribed", "pending", "none") |
43 | 43 |
44 # TODO: need to split this class in several modules, plugin should handle subcommands | 44 # TODO: need to split this class in several modules, plugin should handle subcommands |
45 | 45 |
51 host, | 51 host, |
52 "info", | 52 "info", |
53 use_output=C.OUTPUT_DICT, | 53 use_output=C.OUTPUT_DICT, |
54 use_pubsub=True, | 54 use_pubsub=True, |
55 pubsub_flags={C.NODE}, | 55 pubsub_flags={C.NODE}, |
56 help=_(u"retrieve node configuration"), | 56 help=_("retrieve node configuration"), |
57 ) | 57 ) |
58 self.need_loop = True | 58 self.need_loop = True |
59 | 59 |
60 def add_parser_options(self): | 60 def add_parser_options(self): |
61 self.parser.add_argument( | 61 self.parser.add_argument( |
62 "-k", | 62 "-k", |
63 "--key", | 63 "--key", |
64 type=base.unicode_decoder, | |
65 action="append", | 64 action="append", |
66 dest="keys", | 65 dest="keys", |
67 help=_(u"data key to filter"), | 66 help=_("data key to filter"), |
68 ) | 67 ) |
69 | 68 |
70 def removePrefix(self, key): | 69 def removePrefix(self, key): |
71 return key[7:] if key.startswith(u"pubsub#") else key | 70 return key[7:] if key.startswith("pubsub#") else key |
72 | 71 |
73 def filterKey(self, key): | 72 def filterKey(self, key): |
74 return any((key == k or key == u"pubsub#" + k) for k in self.args.keys) | 73 return any((key == k or key == "pubsub#" + k) for k in self.args.keys) |
75 | 74 |
76 def psNodeConfigurationGetCb(self, config_dict): | 75 def psNodeConfigurationGetCb(self, config_dict): |
77 key_filter = (lambda k: True) if not self.args.keys else self.filterKey | 76 key_filter = (lambda k: True) if not self.args.keys else self.filterKey |
78 config_dict = { | 77 config_dict = { |
79 self.removePrefix(k): v for k, v in config_dict.iteritems() if key_filter(k) | 78 self.removePrefix(k): v for k, v in config_dict.items() if key_filter(k) |
80 } | 79 } |
81 self.output(config_dict) | 80 self.output(config_dict) |
82 self.host.quit() | 81 self.host.quit() |
83 | 82 |
84 def psNodeConfigurationGetEb(self, failure_): | 83 def psNodeConfigurationGetEb(self, failure_): |
85 self.disp( | 84 self.disp( |
86 u"can't get node configuration: {reason}".format(reason=failure_), error=True | 85 "can't get node configuration: {reason}".format(reason=failure_), error=True |
87 ) | 86 ) |
88 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 87 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
89 | 88 |
90 def start(self): | 89 def start(self): |
91 self.host.bridge.psNodeConfigurationGet( | 90 self.host.bridge.psNodeConfigurationGet( |
105 "create", | 104 "create", |
106 use_output=C.OUTPUT_DICT, | 105 use_output=C.OUTPUT_DICT, |
107 use_pubsub=True, | 106 use_pubsub=True, |
108 pubsub_flags={C.NODE}, | 107 pubsub_flags={C.NODE}, |
109 use_verbose=True, | 108 use_verbose=True, |
110 help=_(u"create a node"), | 109 help=_("create a node"), |
111 ) | 110 ) |
112 self.need_loop = True | 111 self.need_loop = True |
113 | 112 |
114 def add_parser_options(self): | 113 def add_parser_options(self): |
115 self.parser.add_argument( | 114 self.parser.add_argument( |
116 "-f", | 115 "-f", |
117 "--field", | 116 "--field", |
118 type=base.unicode_decoder, | |
119 action="append", | 117 action="append", |
120 nargs=2, | 118 nargs=2, |
121 dest="fields", | 119 dest="fields", |
122 default=[], | 120 default=[], |
123 metavar=(u"KEY", u"VALUE"), | 121 metavar=("KEY", "VALUE"), |
124 help=_(u"configuration field to set"), | 122 help=_("configuration field to set"), |
125 ) | 123 ) |
126 self.parser.add_argument( | 124 self.parser.add_argument( |
127 "-F", | 125 "-F", |
128 "--full-prefix", | 126 "--full-prefix", |
129 action="store_true", | 127 action="store_true", |
130 help=_(u'don\'t prepend "pubsub#" prefix to field names'), | 128 help=_('don\'t prepend "pubsub#" prefix to field names'), |
131 ) | 129 ) |
132 | 130 |
133 def psNodeCreateCb(self, node_id): | 131 def psNodeCreateCb(self, node_id): |
134 if self.host.verbosity: | 132 if self.host.verbosity: |
135 announce = _(u"node created successfully: ") | 133 announce = _("node created successfully: ") |
136 else: | 134 else: |
137 announce = u"" | 135 announce = "" |
138 self.disp(announce + node_id) | 136 self.disp(announce + node_id) |
139 self.host.quit() | 137 self.host.quit() |
140 | 138 |
141 def psNodeCreateEb(self, failure_): | 139 def psNodeCreateEb(self, failure_): |
142 self.disp(u"can't create: {reason}".format(reason=failure_), error=True) | 140 self.disp("can't create: {reason}".format(reason=failure_), error=True) |
143 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 141 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
144 | 142 |
145 def start(self): | 143 def start(self): |
146 if not self.args.full_prefix: | 144 if not self.args.full_prefix: |
147 options = {u"pubsub#" + k: v for k, v in self.args.fields} | 145 options = {"pubsub#" + k: v for k, v in self.args.fields} |
148 else: | 146 else: |
149 options = dict(self.args.fields) | 147 options = dict(self.args.fields) |
150 self.host.bridge.psNodeCreate( | 148 self.host.bridge.psNodeCreate( |
151 self.args.service, | 149 self.args.service, |
152 self.args.node, | 150 self.args.node, |
153 options, | 151 options, |
154 self.profile, | 152 self.profile, |
155 callback=self.psNodeCreateCb, | 153 callback=self.psNodeCreateCb, |
156 errback=partial( | 154 errback=partial( |
157 self.errback, | 155 self.errback, |
158 msg=_(u"can't create node: {}"), | 156 msg=_("can't create node: {}"), |
159 exit_code=C.EXIT_BRIDGE_ERRBACK, | 157 exit_code=C.EXIT_BRIDGE_ERRBACK, |
160 ), | 158 ), |
161 ) | 159 ) |
162 | 160 |
163 | 161 |
167 super(NodePurge, self).__init__( | 165 super(NodePurge, self).__init__( |
168 host, | 166 host, |
169 "purge", | 167 "purge", |
170 use_pubsub=True, | 168 use_pubsub=True, |
171 pubsub_flags={C.NODE}, | 169 pubsub_flags={C.NODE}, |
172 help=_(u"purge a node (i.e. remove all items from it)"), | 170 help=_("purge a node (i.e. remove all items from it)"), |
173 ) | 171 ) |
174 self.need_loop = True | 172 self.need_loop = True |
175 | 173 |
176 def add_parser_options(self): | 174 def add_parser_options(self): |
177 self.parser.add_argument( | 175 self.parser.add_argument( |
178 "-f", | 176 "-f", |
179 "--force", | 177 "--force", |
180 action="store_true", | 178 action="store_true", |
181 help=_(u"purge node without confirmation"), | 179 help=_("purge node without confirmation"), |
182 ) | 180 ) |
183 | 181 |
184 def psNodePurgeCb(self): | 182 def psNodePurgeCb(self): |
185 self.disp(_(u"node [{node}] purged successfully").format(node=self.args.node)) | 183 self.disp(_("node [{node}] purged successfully").format(node=self.args.node)) |
186 self.host.quit() | 184 self.host.quit() |
187 | 185 |
188 def start(self): | 186 def start(self): |
189 if not self.args.force: | 187 if not self.args.force: |
190 if not self.args.service: | 188 if not self.args.service: |
191 message = _(u"Are you sure to purge PEP node [{node_id}]? " | 189 message = _("Are you sure to purge PEP node [{node_id}]? " |
192 u"This will delete ALL items from it!").format( | 190 "This will delete ALL items from it!").format( |
193 node_id=self.args.node | 191 node_id=self.args.node |
194 ) | 192 ) |
195 else: | 193 else: |
196 message = _( | 194 message = _( |
197 u"Are you sure to delete node [{node_id}] on service [{service}]? " | 195 "Are you sure to delete node [{node_id}] on service [{service}]? " |
198 u"This will delete ALL items from it!" | 196 "This will delete ALL items from it!" |
199 ).format(node_id=self.args.node, service=self.args.service) | 197 ).format(node_id=self.args.node, service=self.args.service) |
200 self.host.confirmOrQuit(message, _(u"node purge cancelled")) | 198 self.host.confirmOrQuit(message, _("node purge cancelled")) |
201 | 199 |
202 self.host.bridge.psNodePurge( | 200 self.host.bridge.psNodePurge( |
203 self.args.service, | 201 self.args.service, |
204 self.args.node, | 202 self.args.node, |
205 self.profile, | 203 self.profile, |
206 callback=self.psNodePurgeCb, | 204 callback=self.psNodePurgeCb, |
207 errback=partial( | 205 errback=partial( |
208 self.errback, | 206 self.errback, |
209 msg=_(u"can't purge node: {}"), | 207 msg=_("can't purge node: {}"), |
210 exit_code=C.EXIT_BRIDGE_ERRBACK, | 208 exit_code=C.EXIT_BRIDGE_ERRBACK, |
211 ), | 209 ), |
212 ) | 210 ) |
213 | 211 |
214 | 212 |
218 self, | 216 self, |
219 host, | 217 host, |
220 "delete", | 218 "delete", |
221 use_pubsub=True, | 219 use_pubsub=True, |
222 pubsub_flags={C.NODE}, | 220 pubsub_flags={C.NODE}, |
223 help=_(u"delete a node"), | 221 help=_("delete a node"), |
224 ) | 222 ) |
225 self.need_loop = True | 223 self.need_loop = True |
226 | 224 |
227 def add_parser_options(self): | 225 def add_parser_options(self): |
228 self.parser.add_argument( | 226 self.parser.add_argument( |
229 "-f", | 227 "-f", |
230 "--force", | 228 "--force", |
231 action="store_true", | 229 action="store_true", |
232 help=_(u"delete node without confirmation"), | 230 help=_("delete node without confirmation"), |
233 ) | 231 ) |
234 | 232 |
235 def psNodeDeleteCb(self): | 233 def psNodeDeleteCb(self): |
236 self.disp(_(u"node [{node}] deleted successfully").format(node=self.args.node)) | 234 self.disp(_("node [{node}] deleted successfully").format(node=self.args.node)) |
237 self.host.quit() | 235 self.host.quit() |
238 | 236 |
239 def start(self): | 237 def start(self): |
240 if not self.args.force: | 238 if not self.args.force: |
241 if not self.args.service: | 239 if not self.args.service: |
242 message = _(u"Are you sure to delete PEP node [{node_id}] ?").format( | 240 message = _("Are you sure to delete PEP node [{node_id}] ?").format( |
243 node_id=self.args.node | 241 node_id=self.args.node |
244 ) | 242 ) |
245 else: | 243 else: |
246 message = _( | 244 message = _( |
247 u"Are you sure to delete node [{node_id}] on service [{service}] ?" | 245 "Are you sure to delete node [{node_id}] on service [{service}] ?" |
248 ).format(node_id=self.args.node, service=self.args.service) | 246 ).format(node_id=self.args.node, service=self.args.service) |
249 self.host.confirmOrQuit(message, _(u"node deletion cancelled")) | 247 self.host.confirmOrQuit(message, _("node deletion cancelled")) |
250 | 248 |
251 self.host.bridge.psNodeDelete( | 249 self.host.bridge.psNodeDelete( |
252 self.args.service, | 250 self.args.service, |
253 self.args.node, | 251 self.args.node, |
254 self.profile, | 252 self.profile, |
255 callback=self.psNodeDeleteCb, | 253 callback=self.psNodeDeleteCb, |
256 errback=partial( | 254 errback=partial( |
257 self.errback, | 255 self.errback, |
258 msg=_(u"can't delete node: {}"), | 256 msg=_("can't delete node: {}"), |
259 exit_code=C.EXIT_BRIDGE_ERRBACK, | 257 exit_code=C.EXIT_BRIDGE_ERRBACK, |
260 ), | 258 ), |
261 ) | 259 ) |
262 | 260 |
263 | 261 |
269 "set", | 267 "set", |
270 use_output=C.OUTPUT_DICT, | 268 use_output=C.OUTPUT_DICT, |
271 use_pubsub=True, | 269 use_pubsub=True, |
272 pubsub_flags={C.NODE}, | 270 pubsub_flags={C.NODE}, |
273 use_verbose=True, | 271 use_verbose=True, |
274 help=_(u"set node configuration"), | 272 help=_("set node configuration"), |
275 ) | 273 ) |
276 self.need_loop = True | 274 self.need_loop = True |
277 | 275 |
278 def add_parser_options(self): | 276 def add_parser_options(self): |
279 self.parser.add_argument( | 277 self.parser.add_argument( |
280 "-f", | 278 "-f", |
281 "--field", | 279 "--field", |
282 type=base.unicode_decoder, | |
283 action="append", | 280 action="append", |
284 nargs=2, | 281 nargs=2, |
285 dest="fields", | 282 dest="fields", |
286 required=True, | 283 required=True, |
287 metavar=(u"KEY", u"VALUE"), | 284 metavar=("KEY", "VALUE"), |
288 help=_(u"configuration field to set (required)"), | 285 help=_("configuration field to set (required)"), |
289 ) | 286 ) |
290 | 287 |
291 def psNodeConfigurationSetCb(self): | 288 def psNodeConfigurationSetCb(self): |
292 self.disp(_(u"node configuration successful"), 1) | 289 self.disp(_("node configuration successful"), 1) |
293 self.host.quit() | 290 self.host.quit() |
294 | 291 |
295 def psNodeConfigurationSetEb(self, failure_): | 292 def psNodeConfigurationSetEb(self, failure_): |
296 self.disp( | 293 self.disp( |
297 u"can't set node configuration: {reason}".format(reason=failure_), error=True | 294 "can't set node configuration: {reason}".format(reason=failure_), error=True |
298 ) | 295 ) |
299 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 296 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
300 | 297 |
301 def getKeyName(self, k): | 298 def getKeyName(self, k): |
302 if not k.startswith(u"pubsub#"): | 299 if not k.startswith("pubsub#"): |
303 return u"pubsub#" + k | 300 return "pubsub#" + k |
304 else: | 301 else: |
305 return k | 302 return k |
306 | 303 |
307 def start(self): | 304 def start(self): |
308 self.host.bridge.psNodeConfigurationSet( | 305 self.host.bridge.psNodeConfigurationSet( |
321 super(NodeImport, self).__init__( | 318 super(NodeImport, self).__init__( |
322 host, | 319 host, |
323 "import", | 320 "import", |
324 use_pubsub=True, | 321 use_pubsub=True, |
325 pubsub_flags={C.NODE}, | 322 pubsub_flags={C.NODE}, |
326 help=_(u"import raw XML to a node"), | 323 help=_("import raw XML to a node"), |
327 ) | 324 ) |
328 self.need_loop = True | 325 self.need_loop = True |
329 | 326 |
330 def add_parser_options(self): | 327 def add_parser_options(self): |
331 self.parser.add_argument( | 328 self.parser.add_argument( |
332 "--admin", | 329 "--admin", |
333 action="store_true", | 330 action="store_true", |
334 help=_(u"do a pubsub admin request, needed to change publisher"), | 331 help=_("do a pubsub admin request, needed to change publisher"), |
335 ) | 332 ) |
336 self.parser.add_argument( | 333 self.parser.add_argument( |
337 "import_file", | 334 "import_file", |
338 type=file, | 335 type=argparse.FileType(), |
339 help=_(u"path to the XML file with data to import. The file must contain " | 336 help=_("path to the XML file with data to import. The file must contain " |
340 u"whole XML of each item to import."), | 337 "whole XML of each item to import."), |
341 ) | 338 ) |
342 | 339 |
343 def psItemsSendCb(self, item_ids): | 340 def psItemsSendCb(self, item_ids): |
344 self.disp(_(u'items published with id(s) {item_ids}').format( | 341 self.disp(_('items published with id(s) {item_ids}').format( |
345 item_ids=u', '.join(item_ids))) | 342 item_ids=', '.join(item_ids))) |
346 self.host.quit() | 343 self.host.quit() |
347 | 344 |
348 def start(self): | 345 def start(self): |
349 try: | 346 try: |
350 element, etree = xml_tools.etreeParse(self, self.args.import_file, | 347 element, etree = xml_tools.etreeParse(self, self.args.import_file, |
362 # TODO: make this more explicit and add an option | 359 # TODO: make this more explicit and add an option |
363 element[:] = reversed(element) | 360 element[:] = reversed(element) |
364 | 361 |
365 if not all([i.tag == '{http://jabber.org/protocol/pubsub}item' for i in element]): | 362 if not all([i.tag == '{http://jabber.org/protocol/pubsub}item' for i in element]): |
366 self.disp( | 363 self.disp( |
367 _(u"You are not using list of pubsub items, we can't import this file"), | 364 _("You are not using list of pubsub items, we can't import this file"), |
368 error=True) | 365 error=True) |
369 self.host.quit(C.EXIT_DATA_ERROR) | 366 self.host.quit(C.EXIT_DATA_ERROR) |
370 | 367 |
371 items = [etree.tostring(i, encoding="utf-8") for i in element] | 368 items = [etree.tostring(i, encoding="utf-8") for i in element] |
372 if self.args.admin: | 369 if self.args.admin: |
373 self.host.bridge.psAdminItemsSend( | 370 self.host.bridge.psAdminItemsSend( |
374 self.args.service, | 371 self.args.service, |
375 self.args.node, | 372 self.args.node, |
376 items, | 373 items, |
377 u"", | 374 "", |
378 self.profile, | 375 self.profile, |
379 callback=partial(self.psItemsSendCb), | 376 callback=partial(self.psItemsSendCb), |
380 errback=partial( | 377 errback=partial( |
381 self.errback, | 378 self.errback, |
382 msg=_(u"can't send item: {}"), | 379 msg=_("can't send item: {}"), |
383 exit_code=C.EXIT_BRIDGE_ERRBACK, | 380 exit_code=C.EXIT_BRIDGE_ERRBACK, |
384 ), | 381 ), |
385 ) | 382 ) |
386 else: | 383 else: |
387 self.disp(_(u"Items are imported without using admin mode, publisher can't " | 384 self.disp(_("Items are imported without using admin mode, publisher can't " |
388 u"be changed")) | 385 "be changed")) |
389 self.host.bridge.psItemsSend( | 386 self.host.bridge.psItemsSend( |
390 self.args.service, | 387 self.args.service, |
391 self.args.node, | 388 self.args.node, |
392 items, | 389 items, |
393 u"", | 390 "", |
394 self.profile, | 391 self.profile, |
395 callback=partial(self.psItemsSendCb), | 392 callback=partial(self.psItemsSendCb), |
396 errback=partial( | 393 errback=partial( |
397 self.errback, | 394 self.errback, |
398 msg=_(u"can't send item: {}"), | 395 msg=_("can't send item: {}"), |
399 exit_code=C.EXIT_BRIDGE_ERRBACK, | 396 exit_code=C.EXIT_BRIDGE_ERRBACK, |
400 ), | 397 ), |
401 ) | 398 ) |
402 | 399 |
403 | 400 |
408 host, | 405 host, |
409 "get", | 406 "get", |
410 use_output=C.OUTPUT_DICT, | 407 use_output=C.OUTPUT_DICT, |
411 use_pubsub=True, | 408 use_pubsub=True, |
412 pubsub_flags={C.NODE}, | 409 pubsub_flags={C.NODE}, |
413 help=_(u"retrieve node affiliations (for node owner)"), | 410 help=_("retrieve node affiliations (for node owner)"), |
414 ) | 411 ) |
415 self.need_loop = True | 412 self.need_loop = True |
416 | 413 |
417 def add_parser_options(self): | 414 def add_parser_options(self): |
418 pass | 415 pass |
421 self.output(affiliations) | 418 self.output(affiliations) |
422 self.host.quit() | 419 self.host.quit() |
423 | 420 |
424 def psNodeAffiliationsGetEb(self, failure_): | 421 def psNodeAffiliationsGetEb(self, failure_): |
425 self.disp( | 422 self.disp( |
426 u"can't get node affiliations: {reason}".format(reason=failure_), error=True | 423 "can't get node affiliations: {reason}".format(reason=failure_), error=True |
427 ) | 424 ) |
428 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 425 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
429 | 426 |
430 def start(self): | 427 def start(self): |
431 self.host.bridge.psNodeAffiliationsGet( | 428 self.host.bridge.psNodeAffiliationsGet( |
444 host, | 441 host, |
445 "set", | 442 "set", |
446 use_pubsub=True, | 443 use_pubsub=True, |
447 pubsub_flags={C.NODE}, | 444 pubsub_flags={C.NODE}, |
448 use_verbose=True, | 445 use_verbose=True, |
449 help=_(u"set affiliations (for node owner)"), | 446 help=_("set affiliations (for node owner)"), |
450 ) | 447 ) |
451 self.need_loop = True | 448 self.need_loop = True |
452 | 449 |
453 def add_parser_options(self): | 450 def add_parser_options(self): |
454 # XXX: we use optional argument syntax for a required one because list of list of 2 elements | 451 # XXX: we use optional argument syntax for a required one because list of list of 2 elements |
457 "-a", | 454 "-a", |
458 "--affiliation", | 455 "--affiliation", |
459 dest="affiliations", | 456 dest="affiliations", |
460 metavar=("JID", "AFFILIATION"), | 457 metavar=("JID", "AFFILIATION"), |
461 required=True, | 458 required=True, |
462 type=base.unicode_decoder, | |
463 action="append", | 459 action="append", |
464 nargs=2, | 460 nargs=2, |
465 help=_(u"entity/affiliation couple(s)"), | 461 help=_("entity/affiliation couple(s)"), |
466 ) | 462 ) |
467 | 463 |
468 def psNodeAffiliationsSetCb(self): | 464 def psNodeAffiliationsSetCb(self): |
469 self.disp(_(u"affiliations have been set"), 1) | 465 self.disp(_("affiliations have been set"), 1) |
470 self.host.quit() | 466 self.host.quit() |
471 | 467 |
472 def psNodeAffiliationsSetEb(self, failure_): | 468 def psNodeAffiliationsSetEb(self, failure_): |
473 self.disp( | 469 self.disp( |
474 u"can't set node affiliations: {reason}".format(reason=failure_), error=True | 470 "can't set node affiliations: {reason}".format(reason=failure_), error=True |
475 ) | 471 ) |
476 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 472 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
477 | 473 |
478 def start(self): | 474 def start(self): |
479 affiliations = dict(self.args.affiliations) | 475 affiliations = dict(self.args.affiliations) |
493 def __init__(self, host): | 489 def __init__(self, host): |
494 super(NodeAffiliations, self).__init__( | 490 super(NodeAffiliations, self).__init__( |
495 host, | 491 host, |
496 "affiliations", | 492 "affiliations", |
497 use_profile=False, | 493 use_profile=False, |
498 help=_(u"set or retrieve node affiliations"), | 494 help=_("set or retrieve node affiliations"), |
499 ) | 495 ) |
500 | 496 |
501 | 497 |
502 class NodeSubscriptionsGet(base.CommandBase): | 498 class NodeSubscriptionsGet(base.CommandBase): |
503 def __init__(self, host): | 499 def __init__(self, host): |
506 host, | 502 host, |
507 "get", | 503 "get", |
508 use_output=C.OUTPUT_DICT, | 504 use_output=C.OUTPUT_DICT, |
509 use_pubsub=True, | 505 use_pubsub=True, |
510 pubsub_flags={C.NODE}, | 506 pubsub_flags={C.NODE}, |
511 help=_(u"retrieve node subscriptions (for node owner)"), | 507 help=_("retrieve node subscriptions (for node owner)"), |
512 ) | 508 ) |
513 self.need_loop = True | 509 self.need_loop = True |
514 | 510 |
515 def add_parser_options(self): | 511 def add_parser_options(self): |
516 pass | 512 pass |
519 self.output(subscriptions) | 515 self.output(subscriptions) |
520 self.host.quit() | 516 self.host.quit() |
521 | 517 |
522 def psNodeSubscriptionsGetEb(self, failure_): | 518 def psNodeSubscriptionsGetEb(self, failure_): |
523 self.disp( | 519 self.disp( |
524 u"can't get node subscriptions: {reason}".format(reason=failure_), error=True | 520 "can't get node subscriptions: {reason}".format(reason=failure_), error=True |
525 ) | 521 ) |
526 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 522 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
527 | 523 |
528 def start(self): | 524 def start(self): |
529 self.host.bridge.psNodeSubscriptionsGet( | 525 self.host.bridge.psNodeSubscriptionsGet( |
550 subscription = values.pop(0) | 546 subscription = values.pop(0) |
551 except IndexError: | 547 except IndexError: |
552 subscription = "subscribed" | 548 subscription = "subscribed" |
553 if subscription not in ALLOWED_SUBSCRIPTIONS_OWNER: | 549 if subscription not in ALLOWED_SUBSCRIPTIONS_OWNER: |
554 parser.error( | 550 parser.error( |
555 _(u"subscription must be one of {}").format( | 551 _("subscription must be one of {}").format( |
556 u", ".join(ALLOWED_SUBSCRIPTIONS_OWNER) | 552 ", ".join(ALLOWED_SUBSCRIPTIONS_OWNER) |
557 ) | 553 ) |
558 ) | 554 ) |
559 dest_dict[jid_s] = subscription | 555 dest_dict[jid_s] = subscription |
560 | 556 |
561 | 557 |
566 host, | 562 host, |
567 "set", | 563 "set", |
568 use_pubsub=True, | 564 use_pubsub=True, |
569 pubsub_flags={C.NODE}, | 565 pubsub_flags={C.NODE}, |
570 use_verbose=True, | 566 use_verbose=True, |
571 help=_(u"set/modify subscriptions (for node owner)"), | 567 help=_("set/modify subscriptions (for node owner)"), |
572 ) | 568 ) |
573 self.need_loop = True | 569 self.need_loop = True |
574 | 570 |
575 def add_parser_options(self): | 571 def add_parser_options(self): |
576 # XXX: we use optional argument syntax for a required one because list of list of 2 elements | 572 # XXX: we use optional argument syntax for a required one because list of list of 2 elements |
581 dest="subscriptions", | 577 dest="subscriptions", |
582 default={}, | 578 default={}, |
583 nargs="+", | 579 nargs="+", |
584 metavar=("JID [SUSBSCRIPTION]"), | 580 metavar=("JID [SUSBSCRIPTION]"), |
585 required=True, | 581 required=True, |
586 type=base.unicode_decoder, | |
587 action=StoreSubscriptionAction, | 582 action=StoreSubscriptionAction, |
588 help=_(u"entity/subscription couple(s)"), | 583 help=_("entity/subscription couple(s)"), |
589 ) | 584 ) |
590 | 585 |
591 def psNodeSubscriptionsSetCb(self): | 586 def psNodeSubscriptionsSetCb(self): |
592 self.disp(_(u"subscriptions have been set"), 1) | 587 self.disp(_("subscriptions have been set"), 1) |
593 self.host.quit() | 588 self.host.quit() |
594 | 589 |
595 def psNodeSubscriptionsSetEb(self, failure_): | 590 def psNodeSubscriptionsSetEb(self, failure_): |
596 self.disp( | 591 self.disp( |
597 u"can't set node subscriptions: {reason}".format(reason=failure_), error=True | 592 "can't set node subscriptions: {reason}".format(reason=failure_), error=True |
598 ) | 593 ) |
599 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 594 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
600 | 595 |
601 def start(self): | 596 def start(self): |
602 self.host.bridge.psNodeSubscriptionsSet( | 597 self.host.bridge.psNodeSubscriptionsSet( |
615 def __init__(self, host): | 610 def __init__(self, host): |
616 super(NodeSubscriptions, self).__init__( | 611 super(NodeSubscriptions, self).__init__( |
617 host, | 612 host, |
618 "subscriptions", | 613 "subscriptions", |
619 use_profile=False, | 614 use_profile=False, |
620 help=_(u"get or modify node subscriptions"), | 615 help=_("get or modify node subscriptions"), |
621 ) | 616 ) |
622 | 617 |
623 | 618 |
624 class NodeSchemaSet(base.CommandBase): | 619 class NodeSchemaSet(base.CommandBase): |
625 def __init__(self, host): | 620 def __init__(self, host): |
628 host, | 623 host, |
629 "set", | 624 "set", |
630 use_pubsub=True, | 625 use_pubsub=True, |
631 pubsub_flags={C.NODE}, | 626 pubsub_flags={C.NODE}, |
632 use_verbose=True, | 627 use_verbose=True, |
633 help=_(u"set/replace a schema"), | 628 help=_("set/replace a schema"), |
634 ) | 629 ) |
635 self.need_loop = True | 630 self.need_loop = True |
636 | 631 |
637 def add_parser_options(self): | 632 def add_parser_options(self): |
638 self.parser.add_argument("schema", help=_(u"schema to set (must be XML)")) | 633 self.parser.add_argument("schema", help=_("schema to set (must be XML)")) |
639 | 634 |
640 def psSchemaSetCb(self): | 635 def psSchemaSetCb(self): |
641 self.disp(_(u"schema has been set"), 1) | 636 self.disp(_("schema has been set"), 1) |
642 self.host.quit() | 637 self.host.quit() |
643 | 638 |
644 def start(self): | 639 def start(self): |
645 self.host.bridge.psSchemaSet( | 640 self.host.bridge.psSchemaSet( |
646 self.args.service, | 641 self.args.service, |
648 self.args.schema, | 643 self.args.schema, |
649 self.profile, | 644 self.profile, |
650 callback=self.psSchemaSetCb, | 645 callback=self.psSchemaSetCb, |
651 errback=partial( | 646 errback=partial( |
652 self.errback, | 647 self.errback, |
653 msg=_(u"can't set schema: {}"), | 648 msg=_("can't set schema: {}"), |
654 exit_code=C.EXIT_BRIDGE_ERRBACK, | 649 exit_code=C.EXIT_BRIDGE_ERRBACK, |
655 ), | 650 ), |
656 ) | 651 ) |
657 | 652 |
658 | 653 |
666 "edit", | 661 "edit", |
667 use_pubsub=True, | 662 use_pubsub=True, |
668 pubsub_flags={C.NODE}, | 663 pubsub_flags={C.NODE}, |
669 use_draft=True, | 664 use_draft=True, |
670 use_verbose=True, | 665 use_verbose=True, |
671 help=_(u"edit a schema"), | 666 help=_("edit a schema"), |
672 ) | 667 ) |
673 common.BaseEdit.__init__(self, self.host, PUBSUB_SCHEMA_TMP_DIR) | 668 common.BaseEdit.__init__(self, self.host, PUBSUB_SCHEMA_TMP_DIR) |
674 self.need_loop = True | 669 self.need_loop = True |
675 | 670 |
676 def add_parser_options(self): | 671 def add_parser_options(self): |
677 pass | 672 pass |
678 | 673 |
679 def psSchemaSetCb(self): | 674 def psSchemaSetCb(self): |
680 self.disp(_(u"schema has been set"), 1) | 675 self.disp(_("schema has been set"), 1) |
681 self.host.quit() | 676 self.host.quit() |
682 | 677 |
683 def publish(self, schema): | 678 def publish(self, schema): |
684 self.host.bridge.psSchemaSet( | 679 self.host.bridge.psSchemaSet( |
685 self.args.service, | 680 self.args.service, |
687 schema, | 682 schema, |
688 self.profile, | 683 self.profile, |
689 callback=self.psSchemaSetCb, | 684 callback=self.psSchemaSetCb, |
690 errback=partial( | 685 errback=partial( |
691 self.errback, | 686 self.errback, |
692 msg=_(u"can't set schema: {}"), | 687 msg=_("can't set schema: {}"), |
693 exit_code=C.EXIT_BRIDGE_ERRBACK, | 688 exit_code=C.EXIT_BRIDGE_ERRBACK, |
694 ), | 689 ), |
695 ) | 690 ) |
696 | 691 |
697 def psSchemaGetCb(self, schema): | 692 def psSchemaGetCb(self, schema): |
698 try: | 693 try: |
699 from lxml import etree | 694 from lxml import etree |
700 except ImportError: | 695 except ImportError: |
701 self.disp(u'lxml module must be installed to use edit, please install it ' | 696 self.disp('lxml module must be installed to use edit, please install it ' |
702 u'with "pip install lxml"', | 697 'with "pip install lxml"', |
703 error=True, | 698 error=True, |
704 ) | 699 ) |
705 self.host.quit(1) | 700 self.host.quit(1) |
706 content_file_obj, content_file_path = self.getTmpFile() | 701 content_file_obj, content_file_path = self.getTmpFile() |
707 schema = schema.strip() | 702 schema = schema.strip() |
720 self.args.node, | 715 self.args.node, |
721 self.profile, | 716 self.profile, |
722 callback=self.psSchemaGetCb, | 717 callback=self.psSchemaGetCb, |
723 errback=partial( | 718 errback=partial( |
724 self.errback, | 719 self.errback, |
725 msg=_(u"can't edit schema: {}"), | 720 msg=_("can't edit schema: {}"), |
726 exit_code=C.EXIT_BRIDGE_ERRBACK, | 721 exit_code=C.EXIT_BRIDGE_ERRBACK, |
727 ), | 722 ), |
728 ) | 723 ) |
729 | 724 |
730 | 725 |
736 "get", | 731 "get", |
737 use_output=C.OUTPUT_XML, | 732 use_output=C.OUTPUT_XML, |
738 use_pubsub=True, | 733 use_pubsub=True, |
739 pubsub_flags={C.NODE}, | 734 pubsub_flags={C.NODE}, |
740 use_verbose=True, | 735 use_verbose=True, |
741 help=_(u"get schema"), | 736 help=_("get schema"), |
742 ) | 737 ) |
743 self.need_loop = True | 738 self.need_loop = True |
744 | 739 |
745 def add_parser_options(self): | 740 def add_parser_options(self): |
746 pass | 741 pass |
747 | 742 |
748 def psSchemaGetCb(self, schema): | 743 def psSchemaGetCb(self, schema): |
749 if not schema: | 744 if not schema: |
750 self.disp(_(u"no schema found"), 1) | 745 self.disp(_("no schema found"), 1) |
751 self.host.quit(1) | 746 self.host.quit(1) |
752 self.output(schema) | 747 self.output(schema) |
753 self.host.quit() | 748 self.host.quit() |
754 | 749 |
755 def start(self): | 750 def start(self): |
758 self.args.node, | 753 self.args.node, |
759 self.profile, | 754 self.profile, |
760 callback=self.psSchemaGetCb, | 755 callback=self.psSchemaGetCb, |
761 errback=partial( | 756 errback=partial( |
762 self.errback, | 757 self.errback, |
763 msg=_(u"can't get schema: {}"), | 758 msg=_("can't get schema: {}"), |
764 exit_code=C.EXIT_BRIDGE_ERRBACK, | 759 exit_code=C.EXIT_BRIDGE_ERRBACK, |
765 ), | 760 ), |
766 ) | 761 ) |
767 | 762 |
768 | 763 |
769 class NodeSchema(base.CommandBase): | 764 class NodeSchema(base.CommandBase): |
770 subcommands = (NodeSchemaSet, NodeSchemaEdit, NodeSchemaGet) | 765 subcommands = (NodeSchemaSet, NodeSchemaEdit, NodeSchemaGet) |
771 | 766 |
772 def __init__(self, host): | 767 def __init__(self, host): |
773 super(NodeSchema, self).__init__( | 768 super(NodeSchema, self).__init__( |
774 host, "schema", use_profile=False, help=_(u"data schema manipulation") | 769 host, "schema", use_profile=False, help=_("data schema manipulation") |
775 ) | 770 ) |
776 | 771 |
777 | 772 |
778 class Node(base.CommandBase): | 773 class Node(base.CommandBase): |
779 subcommands = ( | 774 subcommands = ( |
800 self, | 795 self, |
801 host, | 796 host, |
802 "set", | 797 "set", |
803 use_pubsub=True, | 798 use_pubsub=True, |
804 pubsub_flags={C.NODE}, | 799 pubsub_flags={C.NODE}, |
805 help=_(u"publish a new item or update an existing one"), | 800 help=_("publish a new item or update an existing one"), |
806 ) | 801 ) |
807 self.need_loop = True | 802 self.need_loop = True |
808 | 803 |
809 def add_parser_options(self): | 804 def add_parser_options(self): |
810 self.parser.add_argument( | 805 self.parser.add_argument( |
811 "item", | 806 "item", |
812 type=base.unicode_decoder, | |
813 nargs="?", | 807 nargs="?", |
814 default=u"", | 808 default="", |
815 help=_(u"id, URL of the item to update, keyword, or nothing for new item"), | 809 help=_("id, URL of the item to update, keyword, or nothing for new item"), |
816 ) | 810 ) |
817 | 811 |
818 def psItemsSendCb(self, published_id): | 812 def psItemsSendCb(self, published_id): |
819 if published_id: | 813 if published_id: |
820 self.disp(u"Item published at {pub_id}".format(pub_id=published_id)) | 814 self.disp("Item published at {pub_id}".format(pub_id=published_id)) |
821 else: | 815 else: |
822 self.disp(u"Item published") | 816 self.disp("Item published") |
823 self.host.quit(C.EXIT_OK) | 817 self.host.quit(C.EXIT_OK) |
824 | 818 |
825 def start(self): | 819 def start(self): |
826 element, etree = xml_tools.etreeParse(self, sys.stdin) | 820 element, etree = xml_tools.etreeParse(self, sys.stdin) |
827 element = xml_tools.getPayload(self, element) | 821 element = xml_tools.getPayload(self, element) |
835 {}, | 829 {}, |
836 self.profile, | 830 self.profile, |
837 callback=self.psItemsSendCb, | 831 callback=self.psItemsSendCb, |
838 errback=partial( | 832 errback=partial( |
839 self.errback, | 833 self.errback, |
840 msg=_(u"can't send item: {}"), | 834 msg=_("can't send item: {}"), |
841 exit_code=C.EXIT_BRIDGE_ERRBACK, | 835 exit_code=C.EXIT_BRIDGE_ERRBACK, |
842 ), | 836 ), |
843 ) | 837 ) |
844 | 838 |
845 | 839 |
850 host, | 844 host, |
851 "get", | 845 "get", |
852 use_output=C.OUTPUT_LIST_XML, | 846 use_output=C.OUTPUT_LIST_XML, |
853 use_pubsub=True, | 847 use_pubsub=True, |
854 pubsub_flags={C.NODE, C.MULTI_ITEMS}, | 848 pubsub_flags={C.NODE, C.MULTI_ITEMS}, |
855 help=_(u"get pubsub item(s)"), | 849 help=_("get pubsub item(s)"), |
856 ) | 850 ) |
857 self.need_loop = True | 851 self.need_loop = True |
858 | 852 |
859 def add_parser_options(self): | 853 def add_parser_options(self): |
860 self.parser.add_argument( | 854 self.parser.add_argument( |
861 "-S", | 855 "-S", |
862 "--sub-id", | 856 "--sub-id", |
863 type=base.unicode_decoder, | 857 default="", |
864 default=u"", | 858 help=_("subscription id"), |
865 help=_(u"subscription id"), | |
866 ) | 859 ) |
867 # TODO: a key(s) argument to select keys to display | 860 # TODO: a key(s) argument to select keys to display |
868 # TODO: add MAM filters | 861 # TODO: add MAM filters |
869 | 862 |
870 def psItemsGetCb(self, ps_result): | 863 def psItemsGetCb(self, ps_result): |
871 self.output(ps_result[0]) | 864 self.output(ps_result[0]) |
872 self.host.quit(C.EXIT_OK) | 865 self.host.quit(C.EXIT_OK) |
873 | 866 |
874 def psItemsGetEb(self, failure_): | 867 def psItemsGetEb(self, failure_): |
875 self.disp(u"can't get pubsub items: {reason}".format(reason=failure_), error=True) | 868 self.disp("can't get pubsub items: {reason}".format(reason=failure_), error=True) |
876 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 869 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
877 | 870 |
878 def start(self): | 871 def start(self): |
879 self.host.bridge.psItemsGet( | 872 self.host.bridge.psItemsGet( |
880 self.args.service, | 873 self.args.service, |
895 self, | 888 self, |
896 host, | 889 host, |
897 "delete", | 890 "delete", |
898 use_pubsub=True, | 891 use_pubsub=True, |
899 pubsub_flags={C.NODE, C.ITEM, C.SINGLE_ITEM}, | 892 pubsub_flags={C.NODE, C.ITEM, C.SINGLE_ITEM}, |
900 help=_(u"delete an item"), | 893 help=_("delete an item"), |
901 ) | 894 ) |
902 self.need_loop = True | 895 self.need_loop = True |
903 | 896 |
904 def add_parser_options(self): | 897 def add_parser_options(self): |
905 self.parser.add_argument( | 898 self.parser.add_argument( |
906 "-f", "--force", action="store_true", help=_(u"delete without confirmation") | 899 "-f", "--force", action="store_true", help=_("delete without confirmation") |
907 ) | 900 ) |
908 self.parser.add_argument( | 901 self.parser.add_argument( |
909 "-N", "--notify", action="store_true", help=_(u"notify deletion") | 902 "-N", "--notify", action="store_true", help=_("notify deletion") |
910 ) | 903 ) |
911 | 904 |
912 def psItemsDeleteCb(self): | 905 def psItemsDeleteCb(self): |
913 self.disp(_(u"item {item_id} has been deleted").format(item_id=self.args.item)) | 906 self.disp(_("item {item_id} has been deleted").format(item_id=self.args.item)) |
914 self.host.quit(C.EXIT_OK) | 907 self.host.quit(C.EXIT_OK) |
915 | 908 |
916 def start(self): | 909 def start(self): |
917 if not self.args.item: | 910 if not self.args.item: |
918 self.parser.error(_(u"You need to specify an item to delete")) | 911 self.parser.error(_("You need to specify an item to delete")) |
919 if not self.args.force: | 912 if not self.args.force: |
920 message = _(u"Are you sure to delete item {item_id} ?").format( | 913 message = _("Are you sure to delete item {item_id} ?").format( |
921 item_id=self.args.item | 914 item_id=self.args.item |
922 ) | 915 ) |
923 self.host.confirmOrQuit(message, _(u"item deletion cancelled")) | 916 self.host.confirmOrQuit(message, _("item deletion cancelled")) |
924 self.host.bridge.psRetractItem( | 917 self.host.bridge.psRetractItem( |
925 self.args.service, | 918 self.args.service, |
926 self.args.node, | 919 self.args.node, |
927 self.args.item, | 920 self.args.item, |
928 self.args.notify, | 921 self.args.notify, |
929 self.profile, | 922 self.profile, |
930 callback=self.psItemsDeleteCb, | 923 callback=self.psItemsDeleteCb, |
931 errback=partial( | 924 errback=partial( |
932 self.errback, | 925 self.errback, |
933 msg=_(u"can't delete item: {}"), | 926 msg=_("can't delete item: {}"), |
934 exit_code=C.EXIT_BRIDGE_ERRBACK, | 927 exit_code=C.EXIT_BRIDGE_ERRBACK, |
935 ), | 928 ), |
936 ) | 929 ) |
937 | 930 |
938 | 931 |
944 "edit", | 937 "edit", |
945 use_verbose=True, | 938 use_verbose=True, |
946 use_pubsub=True, | 939 use_pubsub=True, |
947 pubsub_flags={C.NODE, C.SINGLE_ITEM}, | 940 pubsub_flags={C.NODE, C.SINGLE_ITEM}, |
948 use_draft=True, | 941 use_draft=True, |
949 help=_(u"edit an existing or new pubsub item"), | 942 help=_("edit an existing or new pubsub item"), |
950 ) | 943 ) |
951 common.BaseEdit.__init__(self, self.host, PUBSUB_TMP_DIR) | 944 common.BaseEdit.__init__(self, self.host, PUBSUB_TMP_DIR) |
952 | 945 |
953 def add_parser_options(self): | 946 def add_parser_options(self): |
954 pass | 947 pass |
965 self.pubsub_item or "", | 958 self.pubsub_item or "", |
966 {}, | 959 {}, |
967 self.profile, | 960 self.profile, |
968 ) | 961 ) |
969 if published_id: | 962 if published_id: |
970 self.disp(u"Item published at {pub_id}".format(pub_id=published_id)) | 963 self.disp("Item published at {pub_id}".format(pub_id=published_id)) |
971 else: | 964 else: |
972 self.disp(u"Item published") | 965 self.disp("Item published") |
973 | 966 |
974 def getItemData(self, service, node, item): | 967 def getItemData(self, service, node, item): |
975 try: | 968 try: |
976 from lxml import etree | 969 from lxml import etree |
977 except ImportError: | 970 except ImportError: |
978 self.disp(u'lxml module must be installed to use edit, please install it ' | 971 self.disp('lxml module must be installed to use edit, please install it ' |
979 u'with "pip install lxml"', | 972 'with "pip install lxml"', |
980 error=True, | 973 error=True, |
981 ) | 974 ) |
982 self.host.quit(1) | 975 self.host.quit(1) |
983 items = [item] if item else [] | 976 items = [item] if item else [] |
984 item_raw = self.host.bridge.psItemsGet( | 977 item_raw = self.host.bridge.psItemsGet( |
988 item_elt = etree.fromstring(item_raw, parser) | 981 item_elt = etree.fromstring(item_raw, parser) |
989 item_id = item_elt.get("id") | 982 item_id = item_elt.get("id") |
990 try: | 983 try: |
991 payload = item_elt[0] | 984 payload = item_elt[0] |
992 except IndexError: | 985 except IndexError: |
993 self.disp(_(u"Item has not payload"), 1) | 986 self.disp(_("Item has not payload"), 1) |
994 return u"" | 987 return "" |
995 return etree.tostring(payload, encoding="unicode", pretty_print=True), item_id | 988 return etree.tostring(payload, encoding="unicode", pretty_print=True), item_id |
996 | 989 |
997 def start(self): | 990 def start(self): |
998 self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj = ( | 991 self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj = ( |
999 self.getItemPath() | 992 self.getItemPath() |
1008 host, | 1001 host, |
1009 "subscribe", | 1002 "subscribe", |
1010 use_pubsub=True, | 1003 use_pubsub=True, |
1011 pubsub_flags={C.NODE}, | 1004 pubsub_flags={C.NODE}, |
1012 use_verbose=True, | 1005 use_verbose=True, |
1013 help=_(u"subscribe to a node"), | 1006 help=_("subscribe to a node"), |
1014 ) | 1007 ) |
1015 self.need_loop = True | 1008 self.need_loop = True |
1016 | 1009 |
1017 def add_parser_options(self): | 1010 def add_parser_options(self): |
1018 pass | 1011 pass |
1019 | 1012 |
1020 def psSubscribeCb(self, sub_id): | 1013 def psSubscribeCb(self, sub_id): |
1021 self.disp(_(u"subscription done"), 1) | 1014 self.disp(_("subscription done"), 1) |
1022 if sub_id: | 1015 if sub_id: |
1023 self.disp(_(u"subscription id: {sub_id}").format(sub_id=sub_id)) | 1016 self.disp(_("subscription id: {sub_id}").format(sub_id=sub_id)) |
1024 self.host.quit() | 1017 self.host.quit() |
1025 | 1018 |
1026 def start(self): | 1019 def start(self): |
1027 self.host.bridge.psSubscribe( | 1020 self.host.bridge.psSubscribe( |
1028 self.args.service, | 1021 self.args.service, |
1030 {}, | 1023 {}, |
1031 self.profile, | 1024 self.profile, |
1032 callback=self.psSubscribeCb, | 1025 callback=self.psSubscribeCb, |
1033 errback=partial( | 1026 errback=partial( |
1034 self.errback, | 1027 self.errback, |
1035 msg=_(u"can't subscribe to node: {}"), | 1028 msg=_("can't subscribe to node: {}"), |
1036 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1029 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1037 ), | 1030 ), |
1038 ) | 1031 ) |
1039 | 1032 |
1040 | 1033 |
1047 host, | 1040 host, |
1048 "unsubscribe", | 1041 "unsubscribe", |
1049 use_pubsub=True, | 1042 use_pubsub=True, |
1050 pubsub_flags={C.NODE}, | 1043 pubsub_flags={C.NODE}, |
1051 use_verbose=True, | 1044 use_verbose=True, |
1052 help=_(u"unsubscribe from a node"), | 1045 help=_("unsubscribe from a node"), |
1053 ) | 1046 ) |
1054 self.need_loop = True | 1047 self.need_loop = True |
1055 | 1048 |
1056 def add_parser_options(self): | 1049 def add_parser_options(self): |
1057 pass | 1050 pass |
1058 | 1051 |
1059 def psUnsubscribeCb(self): | 1052 def psUnsubscribeCb(self): |
1060 self.disp(_(u"subscription removed"), 1) | 1053 self.disp(_("subscription removed"), 1) |
1061 self.host.quit() | 1054 self.host.quit() |
1062 | 1055 |
1063 def start(self): | 1056 def start(self): |
1064 self.host.bridge.psUnsubscribe( | 1057 self.host.bridge.psUnsubscribe( |
1065 self.args.service, | 1058 self.args.service, |
1066 self.args.node, | 1059 self.args.node, |
1067 self.profile, | 1060 self.profile, |
1068 callback=self.psUnsubscribeCb, | 1061 callback=self.psUnsubscribeCb, |
1069 errback=partial( | 1062 errback=partial( |
1070 self.errback, | 1063 self.errback, |
1071 msg=_(u"can't unsubscribe from node: {}"), | 1064 msg=_("can't unsubscribe from node: {}"), |
1072 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1065 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1073 ), | 1066 ), |
1074 ) | 1067 ) |
1075 | 1068 |
1076 | 1069 |
1080 self, | 1073 self, |
1081 host, | 1074 host, |
1082 "subscriptions", | 1075 "subscriptions", |
1083 use_output=C.OUTPUT_LIST_DICT, | 1076 use_output=C.OUTPUT_LIST_DICT, |
1084 use_pubsub=True, | 1077 use_pubsub=True, |
1085 help=_(u"retrieve all subscriptions on a service"), | 1078 help=_("retrieve all subscriptions on a service"), |
1086 ) | 1079 ) |
1087 self.need_loop = True | 1080 self.need_loop = True |
1088 | 1081 |
1089 def add_parser_options(self): | 1082 def add_parser_options(self): |
1090 pass | 1083 pass |
1099 self.args.node, | 1092 self.args.node, |
1100 self.profile, | 1093 self.profile, |
1101 callback=self.psSubscriptionsGetCb, | 1094 callback=self.psSubscriptionsGetCb, |
1102 errback=partial( | 1095 errback=partial( |
1103 self.errback, | 1096 self.errback, |
1104 msg=_(u"can't retrieve subscriptions: {}"), | 1097 msg=_("can't retrieve subscriptions: {}"), |
1105 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1098 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1106 ), | 1099 ), |
1107 ) | 1100 ) |
1108 | 1101 |
1109 | 1102 |
1113 self, | 1106 self, |
1114 host, | 1107 host, |
1115 "affiliations", | 1108 "affiliations", |
1116 use_output=C.OUTPUT_DICT, | 1109 use_output=C.OUTPUT_DICT, |
1117 use_pubsub=True, | 1110 use_pubsub=True, |
1118 help=_(u"retrieve all affiliations on a service"), | 1111 help=_("retrieve all affiliations on a service"), |
1119 ) | 1112 ) |
1120 self.need_loop = True | 1113 self.need_loop = True |
1121 | 1114 |
1122 def add_parser_options(self): | 1115 def add_parser_options(self): |
1123 pass | 1116 pass |
1126 self.output(affiliations) | 1119 self.output(affiliations) |
1127 self.host.quit() | 1120 self.host.quit() |
1128 | 1121 |
1129 def psAffiliationsGetEb(self, failure_): | 1122 def psAffiliationsGetEb(self, failure_): |
1130 self.disp( | 1123 self.disp( |
1131 u"can't get node affiliations: {reason}".format(reason=failure_), error=True | 1124 "can't get node affiliations: {reason}".format(reason=failure_), error=True |
1132 ) | 1125 ) |
1133 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | 1126 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
1134 | 1127 |
1135 def start(self): | 1128 def start(self): |
1136 self.host.bridge.psAffiliationsGet( | 1129 self.host.bridge.psAffiliationsGet( |
1148 This commands checks every items it finds by itself, | 1141 This commands checks every items it finds by itself, |
1149 so it may be heavy in resources both for server and client | 1142 so it may be heavy in resources both for server and client |
1150 """ | 1143 """ |
1151 | 1144 |
1152 RE_FLAGS = re.MULTILINE | re.UNICODE | 1145 RE_FLAGS = re.MULTILINE | re.UNICODE |
1153 EXEC_ACTIONS = (u"exec", u"external") | 1146 EXEC_ACTIONS = ("exec", "external") |
1154 | 1147 |
1155 def __init__(self, host): | 1148 def __init__(self, host): |
1156 # FIXME: C.NO_MAX is not needed here, and this can be globally removed from consts | 1149 # FIXME: C.NO_MAX is not needed here, and this can be globally removed from consts |
1157 # the only interest is to change the help string, but this can be explained | 1150 # the only interest is to change the help string, but this can be explained |
1158 # extensively in man pages (max is for each node found) | 1151 # extensively in man pages (max is for each node found) |
1162 "search", | 1155 "search", |
1163 use_output=C.OUTPUT_XML, | 1156 use_output=C.OUTPUT_XML, |
1164 use_pubsub=True, | 1157 use_pubsub=True, |
1165 pubsub_flags={C.MULTI_ITEMS, C.NO_MAX}, | 1158 pubsub_flags={C.MULTI_ITEMS, C.NO_MAX}, |
1166 use_verbose=True, | 1159 use_verbose=True, |
1167 help=_(u"search items corresponding to filters"), | 1160 help=_("search items corresponding to filters"), |
1168 ) | 1161 ) |
1169 self.need_loop = True | 1162 self.need_loop = True |
1170 | 1163 |
1171 @property | 1164 @property |
1172 def etree(self): | 1165 def etree(self): |
1176 | 1169 |
1177 self._etree = etree | 1170 self._etree = etree |
1178 return self._etree | 1171 return self._etree |
1179 | 1172 |
1180 def filter_opt(self, value, type_): | 1173 def filter_opt(self, value, type_): |
1181 value = base.unicode_decoder(value) | |
1182 return (type_, value) | 1174 return (type_, value) |
1183 | 1175 |
1184 def filter_flag(self, value, type_): | 1176 def filter_flag(self, value, type_): |
1185 value = C.bool(value) | 1177 value = C.bool(value) |
1186 return (type_, value) | 1178 return (type_, value) |
1189 self.parser.add_argument( | 1181 self.parser.add_argument( |
1190 "-D", | 1182 "-D", |
1191 "--max-depth", | 1183 "--max-depth", |
1192 type=int, | 1184 type=int, |
1193 default=0, | 1185 default=0, |
1194 help=_(u"maximum depth of recursion (will search linked nodes if > 0, " | 1186 help=_("maximum depth of recursion (will search linked nodes if > 0, " |
1195 u"DEFAULT: 0)"), | 1187 "DEFAULT: 0)"), |
1196 ) | 1188 ) |
1197 self.parser.add_argument( | 1189 self.parser.add_argument( |
1198 "-M", | 1190 "-M", |
1199 "--node-max", | 1191 "--node-max", |
1200 type=int, | 1192 type=int, |
1201 default=30, | 1193 default=30, |
1202 help=_(u"maximum number of items to get per node ({} to get all items, " | 1194 help=_("maximum number of items to get per node ({} to get all items, " |
1203 u"DEFAULT: 30)".format( C.NO_LIMIT)), | 1195 "DEFAULT: 30)".format( C.NO_LIMIT)), |
1204 ) | 1196 ) |
1205 self.parser.add_argument( | 1197 self.parser.add_argument( |
1206 "-N", | 1198 "-N", |
1207 "--namespace", | 1199 "--namespace", |
1208 action="append", | 1200 action="append", |
1209 nargs=2, | 1201 nargs=2, |
1210 default=[], | 1202 default=[], |
1211 metavar="NAME NAMESPACE", | 1203 metavar="NAME NAMESPACE", |
1212 help=_(u"namespace to use for xpath"), | 1204 help=_("namespace to use for xpath"), |
1213 ) | 1205 ) |
1214 | 1206 |
1215 # filters | 1207 # filters |
1216 filter_text = partial(self.filter_opt, type_=u"text") | 1208 filter_text = partial(self.filter_opt, type_="text") |
1217 filter_re = partial(self.filter_opt, type_=u"regex") | 1209 filter_re = partial(self.filter_opt, type_="regex") |
1218 filter_xpath = partial(self.filter_opt, type_=u"xpath") | 1210 filter_xpath = partial(self.filter_opt, type_="xpath") |
1219 filter_python = partial(self.filter_opt, type_=u"python") | 1211 filter_python = partial(self.filter_opt, type_="python") |
1220 filters = self.parser.add_argument_group( | 1212 filters = self.parser.add_argument_group( |
1221 _(u"filters"), | 1213 _("filters"), |
1222 _(u"only items corresponding to following filters will be kept"), | 1214 _("only items corresponding to following filters will be kept"), |
1223 ) | 1215 ) |
1224 filters.add_argument( | 1216 filters.add_argument( |
1225 "-t", | 1217 "-t", |
1226 "--text", | 1218 "--text", |
1227 action="append", | 1219 action="append", |
1228 dest="filters", | 1220 dest="filters", |
1229 type=filter_text, | 1221 type=filter_text, |
1230 metavar="TEXT", | 1222 metavar="TEXT", |
1231 help=_(u"full text filter, item must contain this string (XML included)"), | 1223 help=_("full text filter, item must contain this string (XML included)"), |
1232 ) | 1224 ) |
1233 filters.add_argument( | 1225 filters.add_argument( |
1234 "-r", | 1226 "-r", |
1235 "--regex", | 1227 "--regex", |
1236 action="append", | 1228 action="append", |
1237 dest="filters", | 1229 dest="filters", |
1238 type=filter_re, | 1230 type=filter_re, |
1239 metavar="EXPRESSION", | 1231 metavar="EXPRESSION", |
1240 help=_(u"like --text but using a regular expression"), | 1232 help=_("like --text but using a regular expression"), |
1241 ) | 1233 ) |
1242 filters.add_argument( | 1234 filters.add_argument( |
1243 "-x", | 1235 "-x", |
1244 "--xpath", | 1236 "--xpath", |
1245 action="append", | 1237 action="append", |
1246 dest="filters", | 1238 dest="filters", |
1247 type=filter_xpath, | 1239 type=filter_xpath, |
1248 metavar="XPATH", | 1240 metavar="XPATH", |
1249 help=_(u"filter items which has elements matching this xpath"), | 1241 help=_("filter items which has elements matching this xpath"), |
1250 ) | 1242 ) |
1251 filters.add_argument( | 1243 filters.add_argument( |
1252 "-P", | 1244 "-P", |
1253 "--python", | 1245 "--python", |
1254 action="append", | 1246 action="append", |
1255 dest="filters", | 1247 dest="filters", |
1256 type=filter_python, | 1248 type=filter_python, |
1257 metavar="PYTHON_CODE", | 1249 metavar="PYTHON_CODE", |
1258 help=_(u'Python expression which much return a bool (True to keep item, ' | 1250 help=_('Python expression which much return a bool (True to keep item, ' |
1259 u'False to reject it). "item" is raw text item, "item_xml" is ' | 1251 'False to reject it). "item" is raw text item, "item_xml" is ' |
1260 u'lxml\'s etree.Element' | 1252 'lxml\'s etree.Element' |
1261 ), | 1253 ), |
1262 ) | 1254 ) |
1263 | 1255 |
1264 # filters flags | 1256 # filters flags |
1265 flag_case = partial(self.filter_flag, type_=u"ignore-case") | 1257 flag_case = partial(self.filter_flag, type_="ignore-case") |
1266 flag_invert = partial(self.filter_flag, type_=u"invert") | 1258 flag_invert = partial(self.filter_flag, type_="invert") |
1267 flag_dotall = partial(self.filter_flag, type_=u"dotall") | 1259 flag_dotall = partial(self.filter_flag, type_="dotall") |
1268 flag_matching = partial(self.filter_flag, type_=u"only-matching") | 1260 flag_matching = partial(self.filter_flag, type_="only-matching") |
1269 flags = self.parser.add_argument_group( | 1261 flags = self.parser.add_argument_group( |
1270 _(u"filters flags"), | 1262 _("filters flags"), |
1271 _(u"filters modifiers (change behaviour of following filters)"), | 1263 _("filters modifiers (change behaviour of following filters)"), |
1272 ) | 1264 ) |
1273 flags.add_argument( | 1265 flags.add_argument( |
1274 "-C", | 1266 "-C", |
1275 "--ignore-case", | 1267 "--ignore-case", |
1276 action="append", | 1268 action="append", |
1277 dest="filters", | 1269 dest="filters", |
1278 type=flag_case, | 1270 type=flag_case, |
1279 const=("ignore-case", True), | 1271 const=("ignore-case", True), |
1280 nargs="?", | 1272 nargs="?", |
1281 metavar="BOOLEAN", | 1273 metavar="BOOLEAN", |
1282 help=_(u"(don't) ignore case in following filters (DEFAULT: case sensitive)"), | 1274 help=_("(don't) ignore case in following filters (DEFAULT: case sensitive)"), |
1283 ) | 1275 ) |
1284 flags.add_argument( | 1276 flags.add_argument( |
1285 "-I", | 1277 "-I", |
1286 "--invert", | 1278 "--invert", |
1287 action="append", | 1279 action="append", |
1288 dest="filters", | 1280 dest="filters", |
1289 type=flag_invert, | 1281 type=flag_invert, |
1290 const=("invert", True), | 1282 const=("invert", True), |
1291 nargs="?", | 1283 nargs="?", |
1292 metavar="BOOLEAN", | 1284 metavar="BOOLEAN", |
1293 help=_(u"(don't) invert effect of following filters (DEFAULT: don't invert)"), | 1285 help=_("(don't) invert effect of following filters (DEFAULT: don't invert)"), |
1294 ) | 1286 ) |
1295 flags.add_argument( | 1287 flags.add_argument( |
1296 "-A", | 1288 "-A", |
1297 "--dot-all", | 1289 "--dot-all", |
1298 action="append", | 1290 action="append", |
1299 dest="filters", | 1291 dest="filters", |
1300 type=flag_dotall, | 1292 type=flag_dotall, |
1301 const=("dotall", True), | 1293 const=("dotall", True), |
1302 nargs="?", | 1294 nargs="?", |
1303 metavar="BOOLEAN", | 1295 metavar="BOOLEAN", |
1304 help=_(u"(don't) use DOTALL option for regex (DEFAULT: don't use)"), | 1296 help=_("(don't) use DOTALL option for regex (DEFAULT: don't use)"), |
1305 ) | 1297 ) |
1306 flags.add_argument( | 1298 flags.add_argument( |
1307 "-k", | 1299 "-k", |
1308 "--only-matching", | 1300 "--only-matching", |
1309 action="append", | 1301 action="append", |
1310 dest="filters", | 1302 dest="filters", |
1311 type=flag_matching, | 1303 type=flag_matching, |
1312 const=("only-matching", True), | 1304 const=("only-matching", True), |
1313 nargs="?", | 1305 nargs="?", |
1314 metavar="BOOLEAN", | 1306 metavar="BOOLEAN", |
1315 help=_(u"keep only the matching part of the item"), | 1307 help=_("keep only the matching part of the item"), |
1316 ) | 1308 ) |
1317 | 1309 |
1318 # action | 1310 # action |
1319 self.parser.add_argument( | 1311 self.parser.add_argument( |
1320 "action", | 1312 "action", |
1321 default="print", | 1313 default="print", |
1322 nargs="?", | 1314 nargs="?", |
1323 choices=("print", "exec", "external"), | 1315 choices=("print", "exec", "external"), |
1324 help=_(u"action to do on found items (DEFAULT: print)"), | 1316 help=_("action to do on found items (DEFAULT: print)"), |
1325 ) | 1317 ) |
1326 self.parser.add_argument("command", nargs=argparse.REMAINDER) | 1318 self.parser.add_argument("command", nargs=argparse.REMAINDER) |
1327 | 1319 |
1328 def psItemsGetEb(self, failure_, service, node): | 1320 def psItemsGetEb(self, failure_, service, node): |
1329 self.disp( | 1321 self.disp( |
1330 u"can't get pubsub items at {service} (node: {node}): {reason}".format( | 1322 "can't get pubsub items at {service} (node: {node}): {reason}".format( |
1331 service=service, node=node, reason=failure_ | 1323 service=service, node=node, reason=failure_ |
1332 ), | 1324 ), |
1333 error=True, | 1325 error=True, |
1334 ) | 1326 ) |
1335 self.to_get -= 1 | 1327 self.to_get -= 1 |
1355 | 1347 |
1356 @param found_nodes(list[unicode]): found_nodes | 1348 @param found_nodes(list[unicode]): found_nodes |
1357 this list will be filled while xmpp: URIs are discovered | 1349 this list will be filled while xmpp: URIs are discovered |
1358 """ | 1350 """ |
1359 url = match.group(0) | 1351 url = match.group(0) |
1360 if url.startswith(u"xmpp"): | 1352 if url.startswith("xmpp"): |
1361 try: | 1353 try: |
1362 url_data = uri.parseXMPPUri(url) | 1354 url_data = uri.parseXMPPUri(url) |
1363 except ValueError: | 1355 except ValueError: |
1364 return | 1356 return |
1365 if url_data[u"type"] == u"pubsub": | 1357 if url_data["type"] == "pubsub": |
1366 found_node = {u"service": url_data[u"path"], u"node": url_data[u"node"]} | 1358 found_node = {"service": url_data["path"], "node": url_data["node"]} |
1367 if u"item" in url_data: | 1359 if "item" in url_data: |
1368 found_node[u"item"] = url_data[u"item"] | 1360 found_node["item"] = url_data["item"] |
1369 found_nodes.append(found_node) | 1361 found_nodes.append(found_node) |
1370 | 1362 |
1371 def getSubNodes(self, item, depth): | 1363 def getSubNodes(self, item, depth): |
1372 """look for pubsub URIs in item, and getItems on the linked nodes""" | 1364 """look for pubsub URIs in item, and getItems on the linked nodes""" |
1373 found_nodes = [] | 1365 found_nodes = [] |
1374 checkURI = partial(self._checkPubsubURL, found_nodes=found_nodes) | 1366 checkURI = partial(self._checkPubsubURL, found_nodes=found_nodes) |
1375 strings.RE_URL.sub(checkURI, item) | 1367 strings.RE_URL.sub(checkURI, item) |
1376 for data in found_nodes: | 1368 for data in found_nodes: |
1377 self.getItems( | 1369 self.getItems( |
1378 depth + 1, | 1370 depth + 1, |
1379 data[u"service"], | 1371 data["service"], |
1380 data[u"node"], | 1372 data["node"], |
1381 [data[u"item"]] if u"item" in data else [], | 1373 [data["item"]] if "item" in data else [], |
1382 ) | 1374 ) |
1383 | 1375 |
1384 def parseXml(self, item): | 1376 def parseXml(self, item): |
1385 try: | 1377 try: |
1386 return self.etree.fromstring(item) | 1378 return self.etree.fromstring(item) |
1387 except self.etree.XMLSyntaxError: | 1379 except self.etree.XMLSyntaxError: |
1388 self.disp( | 1380 self.disp( |
1389 _(u"item doesn't looks like XML, you have probably used --only-matching " | 1381 _("item doesn't looks like XML, you have probably used --only-matching " |
1390 u"somewhere before and we have no more XML"), | 1382 "somewhere before and we have no more XML"), |
1391 error=True, | 1383 error=True, |
1392 ) | 1384 ) |
1393 self.host.quit(C.EXIT_BAD_ARG) | 1385 self.host.quit(C.EXIT_BAD_ARG) |
1394 | 1386 |
1395 def filter(self, item): | 1387 def filter(self, item): |
1408 for type_, value in self.args.filters: | 1400 for type_, value in self.args.filters: |
1409 keep = True | 1401 keep = True |
1410 | 1402 |
1411 ## filters | 1403 ## filters |
1412 | 1404 |
1413 if type_ == u"text": | 1405 if type_ == "text": |
1414 if ignore_case: | 1406 if ignore_case: |
1415 if value.lower() not in item.lower(): | 1407 if value.lower() not in item.lower(): |
1416 keep = False | 1408 keep = False |
1417 else: | 1409 else: |
1418 if value not in item: | 1410 if value not in item: |
1420 if keep and only_matching: | 1412 if keep and only_matching: |
1421 # doesn't really make sens to keep a fixed string | 1413 # doesn't really make sens to keep a fixed string |
1422 # so we raise an error | 1414 # so we raise an error |
1423 self.host.disp( | 1415 self.host.disp( |
1424 _( | 1416 _( |
1425 u"--only-matching used with fixed --text string, are you sure?" | 1417 "--only-matching used with fixed --text string, are you sure?" |
1426 ), | 1418 ), |
1427 error=True, | 1419 error=True, |
1428 ) | 1420 ) |
1429 self.host.quit(C.EXIT_BAD_ARG) | 1421 self.host.quit(C.EXIT_BAD_ARG) |
1430 elif type_ == u"regex": | 1422 elif type_ == "regex": |
1431 flags = self.RE_FLAGS | 1423 flags = self.RE_FLAGS |
1432 if ignore_case: | 1424 if ignore_case: |
1433 flags |= re.IGNORECASE | 1425 flags |= re.IGNORECASE |
1434 if dotall: | 1426 if dotall: |
1435 flags |= re.DOTALL | 1427 flags |= re.DOTALL |
1436 match = re.search(value, item, flags) | 1428 match = re.search(value, item, flags) |
1437 keep = match != None | 1429 keep = match != None |
1438 if keep and only_matching: | 1430 if keep and only_matching: |
1439 item = match.group() | 1431 item = match.group() |
1440 item_xml = None | 1432 item_xml = None |
1441 elif type_ == u"xpath": | 1433 elif type_ == "xpath": |
1442 if item_xml is None: | 1434 if item_xml is None: |
1443 item_xml = self.parseXml(item) | 1435 item_xml = self.parseXml(item) |
1444 try: | 1436 try: |
1445 elts = item_xml.xpath(value, namespaces=self.args.namespace) | 1437 elts = item_xml.xpath(value, namespaces=self.args.namespace) |
1446 except self.etree.XPathEvalError as e: | 1438 except self.etree.XPathEvalError as e: |
1447 self.disp( | 1439 self.disp( |
1448 _(u"can't use xpath: {reason}").format(reason=e), error=True | 1440 _("can't use xpath: {reason}").format(reason=e), error=True |
1449 ) | 1441 ) |
1450 self.host.quit(C.EXIT_BAD_ARG) | 1442 self.host.quit(C.EXIT_BAD_ARG) |
1451 keep = bool(elts) | 1443 keep = bool(elts) |
1452 if keep and only_matching: | 1444 if keep and only_matching: |
1453 item_xml = elts[0] | 1445 item_xml = elts[0] |
1454 try: | 1446 try: |
1455 item = self.etree.tostring(item_xml, encoding="unicode") | 1447 item = self.etree.tostring(item_xml, encoding="unicode") |
1456 except TypeError: | 1448 except TypeError: |
1457 # we have a string only, not an element | 1449 # we have a string only, not an element |
1458 item = unicode(item_xml) | 1450 item = str(item_xml) |
1459 item_xml = None | 1451 item_xml = None |
1460 elif type_ == u"python": | 1452 elif type_ == "python": |
1461 if item_xml is None: | 1453 if item_xml is None: |
1462 item_xml = self.parseXml(item) | 1454 item_xml = self.parseXml(item) |
1463 cmd_ns = {u"item": item, u"item_xml": item_xml} | 1455 cmd_ns = {"item": item, "item_xml": item_xml} |
1464 try: | 1456 try: |
1465 keep = eval(value, cmd_ns) | 1457 keep = eval(value, cmd_ns) |
1466 except SyntaxError as e: | 1458 except SyntaxError as e: |
1467 self.disp(unicode(e), error=True) | 1459 self.disp(str(e), error=True) |
1468 self.host.quit(C.EXIT_BAD_ARG) | 1460 self.host.quit(C.EXIT_BAD_ARG) |
1469 | 1461 |
1470 ## flags | 1462 ## flags |
1471 | 1463 |
1472 elif type_ == u"ignore-case": | 1464 elif type_ == "ignore-case": |
1473 ignore_case = value | 1465 ignore_case = value |
1474 elif type_ == u"invert": | 1466 elif type_ == "invert": |
1475 invert = value | 1467 invert = value |
1476 # we need to continue, else loop would end here | 1468 # we need to continue, else loop would end here |
1477 continue | 1469 continue |
1478 elif type_ == u"dotall": | 1470 elif type_ == "dotall": |
1479 dotall = value | 1471 dotall = value |
1480 elif type_ == u"only-matching": | 1472 elif type_ == "only-matching": |
1481 only_matching = value | 1473 only_matching = value |
1482 else: | 1474 else: |
1483 raise exceptions.InternalError( | 1475 raise exceptions.InternalError( |
1484 _(u"unknown filter type {type}").format(type=type_) | 1476 _("unknown filter type {type}").format(type=type_) |
1485 ) | 1477 ) |
1486 | 1478 |
1487 if invert: | 1479 if invert: |
1488 keep = not keep | 1480 keep = not keep |
1489 if not keep: | 1481 if not keep: |
1495 """called when item has been kepts and the action need to be done | 1487 """called when item has been kepts and the action need to be done |
1496 | 1488 |
1497 @param item(unicode): accepted item | 1489 @param item(unicode): accepted item |
1498 """ | 1490 """ |
1499 action = self.args.action | 1491 action = self.args.action |
1500 if action == u"print" or self.host.verbosity > 0: | 1492 if action == "print" or self.host.verbosity > 0: |
1501 try: | 1493 try: |
1502 self.output(item) | 1494 self.output(item) |
1503 except self.etree.XMLSyntaxError: | 1495 except self.etree.XMLSyntaxError: |
1504 # item is not valid XML, but a string | 1496 # item is not valid XML, but a string |
1505 # can happen when --only-matching is used | 1497 # can happen when --only-matching is used |
1506 self.disp(item) | 1498 self.disp(item) |
1507 if action in self.EXEC_ACTIONS: | 1499 if action in self.EXEC_ACTIONS: |
1508 item_elt = self.parseXml(item) | 1500 item_elt = self.parseXml(item) |
1509 if action == u"exec": | 1501 if action == "exec": |
1510 use = { | 1502 use = { |
1511 "service": metadata[u"service"], | 1503 "service": metadata["service"], |
1512 "node": metadata[u"node"], | 1504 "node": metadata["node"], |
1513 "item": item_elt.get("id"), | 1505 "item": item_elt.get("id"), |
1514 "profile": self.profile, | 1506 "profile": self.profile, |
1515 } | 1507 } |
1516 # we need to send a copy of self.args.command | 1508 # we need to send a copy of self.args.command |
1517 # else it would be modified | 1509 # else it would be modified |
1521 cmd_args = sys.argv[0:1] + parser_args + use_args | 1513 cmd_args = sys.argv[0:1] + parser_args + use_args |
1522 else: | 1514 else: |
1523 cmd_args = self.args.command | 1515 cmd_args = self.args.command |
1524 | 1516 |
1525 self.disp( | 1517 self.disp( |
1526 u"COMMAND: {command}".format( | 1518 "COMMAND: {command}".format( |
1527 command=u" ".join([arg_tools.escape(a) for a in cmd_args]) | 1519 command=" ".join([arg_tools.escape(a) for a in cmd_args]) |
1528 ), | 1520 ), |
1529 2, | 1521 2, |
1530 ) | 1522 ) |
1531 if action == u"exec": | 1523 if action == "exec": |
1532 ret = subprocess.call(cmd_args) | 1524 ret = subprocess.call(cmd_args) |
1533 else: | 1525 else: |
1534 p = subprocess.Popen(cmd_args, stdin=subprocess.PIPE) | 1526 p = subprocess.Popen(cmd_args, stdin=subprocess.PIPE) |
1535 p.communicate(item.encode("utf-8")) | 1527 p.communicate(item.encode("utf-8")) |
1536 ret = p.wait() | 1528 ret = p.wait() |
1537 if ret != 0: | 1529 if ret != 0: |
1538 self.disp( | 1530 self.disp( |
1539 A.color( | 1531 A.color( |
1540 C.A_FAILURE, | 1532 C.A_FAILURE, |
1541 _(u"executed command failed with exit code {code}").format( | 1533 _("executed command failed with exit code {code}").format( |
1542 code=ret | 1534 code=ret |
1543 ), | 1535 ), |
1544 ) | 1536 ) |
1545 ) | 1537 ) |
1546 | 1538 |
1571 | 1563 |
1572 def start(self): | 1564 def start(self): |
1573 if self.args.command: | 1565 if self.args.command: |
1574 if self.args.action not in self.EXEC_ACTIONS: | 1566 if self.args.action not in self.EXEC_ACTIONS: |
1575 self.parser.error( | 1567 self.parser.error( |
1576 _(u"Command can only be used with {actions} actions").format( | 1568 _("Command can only be used with {actions} actions").format( |
1577 actions=u", ".join(self.EXEC_ACTIONS) | 1569 actions=", ".join(self.EXEC_ACTIONS) |
1578 ) | 1570 ) |
1579 ) | 1571 ) |
1580 else: | 1572 else: |
1581 if self.args.action in self.EXEC_ACTIONS: | 1573 if self.args.action in self.EXEC_ACTIONS: |
1582 self.parser.error(_(u"you need to specify a command to execute")) | 1574 self.parser.error(_("you need to specify a command to execute")) |
1583 if not self.args.node: | 1575 if not self.args.node: |
1584 # TODO: handle get service affiliations when node is not set | 1576 # TODO: handle get service affiliations when node is not set |
1585 self.parser.error(_(u"empty node is not handled yet")) | 1577 self.parser.error(_("empty node is not handled yet")) |
1586 # to_get is increased on each get and decreased on each answer | 1578 # to_get is increased on each get and decreased on each answer |
1587 # when it reach 0 again, the command is finished | 1579 # when it reach 0 again, the command is finished |
1588 self.to_get = 0 | 1580 self.to_get = 0 |
1589 self._etree = None | 1581 self._etree = None |
1590 if self.args.filters is None: | 1582 if self.args.filters is None: |
1601 self, | 1593 self, |
1602 host, | 1594 host, |
1603 "transform", | 1595 "transform", |
1604 use_pubsub=True, | 1596 use_pubsub=True, |
1605 pubsub_flags={C.NODE, C.MULTI_ITEMS}, | 1597 pubsub_flags={C.NODE, C.MULTI_ITEMS}, |
1606 help=_(u"modify items of a node using an external command/script"), | 1598 help=_("modify items of a node using an external command/script"), |
1607 ) | 1599 ) |
1608 self.need_loop = True | 1600 self.need_loop = True |
1609 | 1601 |
1610 def add_parser_options(self): | 1602 def add_parser_options(self): |
1611 self.parser.add_argument( | 1603 self.parser.add_argument( |
1612 "--apply", | 1604 "--apply", |
1613 action="store_true", | 1605 action="store_true", |
1614 help=_(u"apply transformation (DEFAULT: do a dry run)"), | 1606 help=_("apply transformation (DEFAULT: do a dry run)"), |
1615 ) | 1607 ) |
1616 self.parser.add_argument( | 1608 self.parser.add_argument( |
1617 "--admin", | 1609 "--admin", |
1618 action="store_true", | 1610 action="store_true", |
1619 help=_(u"do a pubsub admin request, needed to change publisher"), | 1611 help=_("do a pubsub admin request, needed to change publisher"), |
1620 ) | 1612 ) |
1621 self.parser.add_argument( | 1613 self.parser.add_argument( |
1622 "-I", | 1614 "-I", |
1623 "--ignore_errors", | 1615 "--ignore_errors", |
1624 action="store_true", | 1616 action="store_true", |
1625 help=_( | 1617 help=_( |
1626 u"if command return a non zero exit code, ignore the item and continue"), | 1618 "if command return a non zero exit code, ignore the item and continue"), |
1627 ) | 1619 ) |
1628 self.parser.add_argument( | 1620 self.parser.add_argument( |
1629 "-A", | 1621 "-A", |
1630 "--all", | 1622 "--all", |
1631 action="store_true", | 1623 action="store_true", |
1632 help=_(u"get all items by looping over all pages using RSM") | 1624 help=_("get all items by looping over all pages using RSM") |
1633 ) | 1625 ) |
1634 self.parser.add_argument( | 1626 self.parser.add_argument( |
1635 "command_path", | 1627 "command_path", |
1636 help=_(u"path to the command to use. Will be called repetitivly with an " | 1628 help=_("path to the command to use. Will be called repetitivly with an " |
1637 u"item as input. Output (full item XML) will be used as new one. " | 1629 "item as input. Output (full item XML) will be used as new one. " |
1638 u'Return "DELETE" string to delete the item, and "SKIP" to ignore it'), | 1630 'Return "DELETE" string to delete the item, and "SKIP" to ignore it'), |
1639 ) | 1631 ) |
1640 | 1632 |
1641 def psItemsSendCb(self, item_ids, metadata): | 1633 def psItemsSendCb(self, item_ids, metadata): |
1642 if item_ids: | 1634 if item_ids: |
1643 self.disp(_(u'items published with ids {item_ids}').format( | 1635 self.disp(_('items published with ids {item_ids}').format( |
1644 item_ids=u', '.join(item_ids))) | 1636 item_ids=', '.join(item_ids))) |
1645 else: | 1637 else: |
1646 self.disp(_(u'items published')) | 1638 self.disp(_('items published')) |
1647 if self.args.all: | 1639 if self.args.all: |
1648 return self.handleNextPage(metadata) | 1640 return self.handleNextPage(metadata) |
1649 else: | 1641 else: |
1650 self.host.quit() | 1642 self.host.quit() |
1651 | 1643 |
1654 | 1646 |
1655 use to handle --all option | 1647 use to handle --all option |
1656 @param metadata(dict): metadata as returned by psItemsGet | 1648 @param metadata(dict): metadata as returned by psItemsGet |
1657 """ | 1649 """ |
1658 try: | 1650 try: |
1659 last = metadata[u'rsm_last'] | 1651 last = metadata['rsm_last'] |
1660 index = int(metadata[u'rsm_index']) | 1652 index = int(metadata['rsm_index']) |
1661 count = int(metadata[u'rsm_count']) | 1653 count = int(metadata['rsm_count']) |
1662 except KeyError: | 1654 except KeyError: |
1663 self.disp(_(u"Can't retrieve all items, RSM metadata not available"), | 1655 self.disp(_("Can't retrieve all items, RSM metadata not available"), |
1664 error=True) | 1656 error=True) |
1665 self.host.quit(C.EXIT_MISSING_FEATURE) | 1657 self.host.quit(C.EXIT_MISSING_FEATURE) |
1666 except ValueError as e: | 1658 except ValueError as e: |
1667 self.disp(_(u"Can't retrieve all items, bad RSM metadata: {msg}") | 1659 self.disp(_("Can't retrieve all items, bad RSM metadata: {msg}") |
1668 .format(msg=e), error=True) | 1660 .format(msg=e), error=True) |
1669 self.host.quit(C.EXIT_ERROR) | 1661 self.host.quit(C.EXIT_ERROR) |
1670 | 1662 |
1671 if index + self.args.rsm_max >= count: | 1663 if index + self.args.rsm_max >= count: |
1672 self.disp(_(u'All items transformed')) | 1664 self.disp(_('All items transformed')) |
1673 self.host.quit(0) | 1665 self.host.quit(0) |
1674 | 1666 |
1675 self.disp(_(u'Retrieving next page ({page_idx}/{page_total})').format( | 1667 self.disp(_('Retrieving next page ({page_idx}/{page_total})').format( |
1676 page_idx = int(index/self.args.rsm_max) + 1, | 1668 page_idx = int(index/self.args.rsm_max) + 1, |
1677 page_total = int(count/self.args.rsm_max), | 1669 page_total = int(count/self.args.rsm_max), |
1678 ) | 1670 ) |
1679 ) | 1671 ) |
1680 | 1672 |
1681 extra = self.getPubsubExtra() | 1673 extra = self.getPubsubExtra() |
1682 extra[u'rsm_after'] = last | 1674 extra['rsm_after'] = last |
1683 self.host.bridge.psItemsGet( | 1675 self.host.bridge.psItemsGet( |
1684 self.args.service, | 1676 self.args.service, |
1685 self.args.node, | 1677 self.args.node, |
1686 self.args.rsm_max, | 1678 self.args.rsm_max, |
1687 self.args.items, | 1679 self.args.items, |
1689 extra, | 1681 extra, |
1690 self.profile, | 1682 self.profile, |
1691 callback=self.psItemsGetCb, | 1683 callback=self.psItemsGetCb, |
1692 errback=partial( | 1684 errback=partial( |
1693 self.errback, | 1685 self.errback, |
1694 msg=_(u"can't retrieve items: {}"), | 1686 msg=_("can't retrieve items: {}"), |
1695 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1687 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1696 ), | 1688 ), |
1697 ) | 1689 ) |
1698 | 1690 |
1699 def psItemsGetCb(self, ps_result): | 1691 def psItemsGetCb(self, ps_result): |
1706 # to avoid infinite loop | 1698 # to avoid infinite loop |
1707 item_elt, __ = xml_tools.etreeParse(self, item) | 1699 item_elt, __ = xml_tools.etreeParse(self, item) |
1708 item_id = item_elt.get('id') | 1700 item_id = item_elt.get('id') |
1709 if item_id in self.items_ids: | 1701 if item_id in self.items_ids: |
1710 self.disp(_( | 1702 self.disp(_( |
1711 u"Duplicate found on item {item_id}, we have probably handled " | 1703 "Duplicate found on item {item_id}, we have probably handled " |
1712 u"all items.").format(item_id=item_id)) | 1704 "all items.").format(item_id=item_id)) |
1713 self.host.quit() | 1705 self.host.quit() |
1714 self.items_ids.append(item_id) | 1706 self.items_ids.append(item_id) |
1715 | 1707 |
1716 # we launch the command to filter the item | 1708 # we launch the command to filter the item |
1717 try: | 1709 try: |
1718 p = subprocess.Popen(self.args.command_path, stdin=subprocess.PIPE, | 1710 p = subprocess.Popen(self.args.command_path, stdin=subprocess.PIPE, |
1719 stdout=subprocess.PIPE) | 1711 stdout=subprocess.PIPE) |
1720 except OSError as e: | 1712 except OSError as e: |
1721 exit_code = C.EXIT_CMD_NOT_FOUND if e.errno == 2 else C.EXIT_ERROR | 1713 exit_code = C.EXIT_CMD_NOT_FOUND if e.errno == 2 else C.EXIT_ERROR |
1722 e = str(e).decode('utf-8', errors="ignore") | 1714 e = str(e).decode('utf-8', errors="ignore") |
1723 self.disp(u"Can't execute the command: {msg}".format(msg=e), error=True) | 1715 self.disp("Can't execute the command: {msg}".format(msg=e), error=True) |
1724 self.host.quit(exit_code) | 1716 self.host.quit(exit_code) |
1725 cmd_std_out, cmd_std_err = p.communicate(item.encode("utf-8")) | 1717 cmd_std_out, cmd_std_err = p.communicate(item.encode("utf-8")) |
1726 ret = p.wait() | 1718 ret = p.wait() |
1727 if ret != 0: | 1719 if ret != 0: |
1728 self.disp(u"The command returned a non zero status while parsing the " | 1720 self.disp("The command returned a non zero status while parsing the " |
1729 u"following item:\n\n{item}".format(item=item), error=True) | 1721 "following item:\n\n{item}".format(item=item), error=True) |
1730 if self.args.ignore_errors: | 1722 if self.args.ignore_errors: |
1731 continue | 1723 continue |
1732 else: | 1724 else: |
1733 self.host.quit(C.EXIT_CMD_ERROR) | 1725 self.host.quit(C.EXIT_CMD_ERROR) |
1734 if cmd_std_err is not None: | 1726 if cmd_std_err is not None: |
1736 self.disp(cmd_std_err, error=True) | 1728 self.disp(cmd_std_err, error=True) |
1737 cmd_std_out = cmd_std_out.strip() | 1729 cmd_std_out = cmd_std_out.strip() |
1738 if cmd_std_out == "DELETE": | 1730 if cmd_std_out == "DELETE": |
1739 item_elt, __ = xml_tools.etreeParse(self, item) | 1731 item_elt, __ = xml_tools.etreeParse(self, item) |
1740 item_id = item_elt.get('id') | 1732 item_id = item_elt.get('id') |
1741 self.disp(_(u"Deleting item {item_id}").format(item_id=item_id)) | 1733 self.disp(_("Deleting item {item_id}").format(item_id=item_id)) |
1742 if self.args.apply: | 1734 if self.args.apply: |
1743 # FIXME: we don't wait for item to be retracted which can cause | 1735 # FIXME: we don't wait for item to be retracted which can cause |
1744 # trouble in case of error just before the end of the command | 1736 # trouble in case of error just before the end of the command |
1745 # (the error message may be missed). | 1737 # (the error message may be missed). |
1746 # Once moved to Python 3, we must wait for it by using a | 1738 # Once moved to Python 3, we must wait for it by using a |
1751 item_id, | 1743 item_id, |
1752 False, | 1744 False, |
1753 self.profile, | 1745 self.profile, |
1754 errback=partial( | 1746 errback=partial( |
1755 self.errback, | 1747 self.errback, |
1756 msg=_(u"can't delete item [%s]: {}" % item_id), | 1748 msg=_("can't delete item [%s]: {}" % item_id), |
1757 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1749 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1758 ), | 1750 ), |
1759 ) | 1751 ) |
1760 continue | 1752 continue |
1761 elif cmd_std_out == "SKIP": | 1753 elif cmd_std_out == "SKIP": |
1762 item_elt, __ = xml_tools.etreeParse(self, item) | 1754 item_elt, __ = xml_tools.etreeParse(self, item) |
1763 item_id = item_elt.get('id') | 1755 item_id = item_elt.get('id') |
1764 self.disp(_(u"Skipping item {item_id}").format(item_id=item_id)) | 1756 self.disp(_("Skipping item {item_id}").format(item_id=item_id)) |
1765 continue | 1757 continue |
1766 element, etree = xml_tools.etreeParse(self, cmd_std_out) | 1758 element, etree = xml_tools.etreeParse(self, cmd_std_out) |
1767 | 1759 |
1768 # at this point command has been run and we have a etree.Element object | 1760 # at this point command has been run and we have a etree.Element object |
1769 if element.tag not in ("item", "{http://jabber.org/protocol/pubsub}item"): | 1761 if element.tag not in ("item", "{http://jabber.org/protocol/pubsub}item"): |
1770 self.disp(u"your script must return a whole item, this is not:\n{xml}" | 1762 self.disp("your script must return a whole item, this is not:\n{xml}" |
1771 .format(xml=etree.tostring(element, encoding="unicode")), error=True) | 1763 .format(xml=etree.tostring(element, encoding="unicode")), error=True) |
1772 self.host.quit(C.EXIT_DATA_ERROR) | 1764 self.host.quit(C.EXIT_DATA_ERROR) |
1773 | 1765 |
1774 if not self.args.apply: | 1766 if not self.args.apply: |
1775 # we have a dry run, we just display filtered items | 1767 # we have a dry run, we just display filtered items |
1776 serialised = etree.tostring(element, encoding=u'unicode', | 1768 serialised = etree.tostring(element, encoding='unicode', |
1777 pretty_print=True) | 1769 pretty_print=True) |
1778 self.disp(serialised) | 1770 self.disp(serialised) |
1779 else: | 1771 else: |
1780 new_items.append(etree.tostring(element, encoding="unicode")) | 1772 new_items.append(etree.tostring(element, encoding="unicode")) |
1781 | 1773 |
1788 if self.args.admin: | 1780 if self.args.admin: |
1789 self.host.bridge.psAdminItemsSend( | 1781 self.host.bridge.psAdminItemsSend( |
1790 self.args.service, | 1782 self.args.service, |
1791 self.args.node, | 1783 self.args.node, |
1792 new_items, | 1784 new_items, |
1793 u"", | 1785 "", |
1794 self.profile, | 1786 self.profile, |
1795 callback=partial(self.psItemsSendCb, metadata=metadata), | 1787 callback=partial(self.psItemsSendCb, metadata=metadata), |
1796 errback=partial( | 1788 errback=partial( |
1797 self.errback, | 1789 self.errback, |
1798 msg=_(u"can't send item: {}"), | 1790 msg=_("can't send item: {}"), |
1799 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1791 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1800 ), | 1792 ), |
1801 ) | 1793 ) |
1802 else: | 1794 else: |
1803 self.host.bridge.psItemsSend( | 1795 self.host.bridge.psItemsSend( |
1804 self.args.service, | 1796 self.args.service, |
1805 self.args.node, | 1797 self.args.node, |
1806 new_items, | 1798 new_items, |
1807 u"", | 1799 "", |
1808 self.profile, | 1800 self.profile, |
1809 callback=partial(self.psItemsSendCb, metadata=metadata), | 1801 callback=partial(self.psItemsSendCb, metadata=metadata), |
1810 errback=partial( | 1802 errback=partial( |
1811 self.errback, | 1803 self.errback, |
1812 msg=_(u"can't send item: {}"), | 1804 msg=_("can't send item: {}"), |
1813 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1805 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1814 ), | 1806 ), |
1815 ) | 1807 ) |
1816 | 1808 |
1817 def start(self): | 1809 def start(self): |
1818 if self.args.all and self.args.order_by != C.ORDER_BY_CREATION: | 1810 if self.args.all and self.args.order_by != C.ORDER_BY_CREATION: |
1819 self.check_duplicates = True | 1811 self.check_duplicates = True |
1820 self.items_ids = [] | 1812 self.items_ids = [] |
1821 self.disp(A.color( | 1813 self.disp(A.color( |
1822 A.FG_RED, A.BOLD, | 1814 A.FG_RED, A.BOLD, |
1823 u'/!\\ "--all" should be used with "--order-by creation" /!\\\n', | 1815 '/!\\ "--all" should be used with "--order-by creation" /!\\\n', |
1824 A.RESET, | 1816 A.RESET, |
1825 u"We'll update items, so order may change during transformation,\n" | 1817 "We'll update items, so order may change during transformation,\n" |
1826 u"we'll try to mitigate that by stopping on first duplicate,\n" | 1818 "we'll try to mitigate that by stopping on first duplicate,\n" |
1827 u"but this method is not safe, and some items may be missed.\n---\n")) | 1819 "but this method is not safe, and some items may be missed.\n---\n")) |
1828 else: | 1820 else: |
1829 self.check_duplicates = False | 1821 self.check_duplicates = False |
1830 self.host.bridge.psItemsGet( | 1822 self.host.bridge.psItemsGet( |
1831 self.args.service, | 1823 self.args.service, |
1832 self.args.node, | 1824 self.args.node, |
1836 self.getPubsubExtra(), | 1828 self.getPubsubExtra(), |
1837 self.profile, | 1829 self.profile, |
1838 callback=self.psItemsGetCb, | 1830 callback=self.psItemsGetCb, |
1839 errback=partial( | 1831 errback=partial( |
1840 self.errback, | 1832 self.errback, |
1841 msg=_(u"can't retrieve items: {}"), | 1833 msg=_("can't retrieve items: {}"), |
1842 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1834 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1843 ), | 1835 ), |
1844 ) | 1836 ) |
1845 | 1837 |
1846 | 1838 |
1851 host, | 1843 host, |
1852 "uri", | 1844 "uri", |
1853 use_profile=False, | 1845 use_profile=False, |
1854 use_pubsub=True, | 1846 use_pubsub=True, |
1855 pubsub_flags={C.NODE, C.SINGLE_ITEM}, | 1847 pubsub_flags={C.NODE, C.SINGLE_ITEM}, |
1856 help=_(u"build URI"), | 1848 help=_("build URI"), |
1857 ) | 1849 ) |
1858 self.need_loop = True | 1850 self.need_loop = True |
1859 | 1851 |
1860 def add_parser_options(self): | 1852 def add_parser_options(self): |
1861 self.parser.add_argument( | 1853 self.parser.add_argument( |
1862 "-p", | 1854 "-p", |
1863 "--profile", | 1855 "--profile", |
1864 type=base.unicode_decoder, | |
1865 default=C.PROF_KEY_DEFAULT, | 1856 default=C.PROF_KEY_DEFAULT, |
1866 help=_(u"profile (used when no server is specified)"), | 1857 help=_("profile (used when no server is specified)"), |
1867 ) | 1858 ) |
1868 | 1859 |
1869 def display_uri(self, jid_): | 1860 def display_uri(self, jid_): |
1870 uri_args = {} | 1861 uri_args = {} |
1871 if not self.args.service: | 1862 if not self.args.service: |
1875 value = getattr(self.args, key) | 1866 value = getattr(self.args, key) |
1876 if key == "service": | 1867 if key == "service": |
1877 key = "path" | 1868 key = "path" |
1878 if value: | 1869 if value: |
1879 uri_args[key] = value | 1870 uri_args[key] = value |
1880 self.disp(uri.buildXMPPUri(u"pubsub", **uri_args)) | 1871 self.disp(uri.buildXMPPUri("pubsub", **uri_args)) |
1881 self.host.quit() | 1872 self.host.quit() |
1882 | 1873 |
1883 def start(self): | 1874 def start(self): |
1884 if not self.args.service: | 1875 if not self.args.service: |
1885 self.host.bridge.asyncGetParamA( | 1876 self.host.bridge.asyncGetParamA( |
1886 u"JabberID", | 1877 "JabberID", |
1887 u"Connection", | 1878 "Connection", |
1888 profile_key=self.args.profile, | 1879 profile_key=self.args.profile, |
1889 callback=self.display_uri, | 1880 callback=self.display_uri, |
1890 errback=partial( | 1881 errback=partial( |
1891 self.errback, | 1882 self.errback, |
1892 msg=_(u"can't retrieve jid: {}"), | 1883 msg=_("can't retrieve jid: {}"), |
1893 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1884 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1894 ), | 1885 ), |
1895 ) | 1886 ) |
1896 else: | 1887 else: |
1897 self.display_uri(None) | 1888 self.display_uri(None) |
1903 self, | 1894 self, |
1904 host, | 1895 host, |
1905 "create", | 1896 "create", |
1906 use_pubsub=True, | 1897 use_pubsub=True, |
1907 pubsub_flags={C.NODE}, | 1898 pubsub_flags={C.NODE}, |
1908 help=_(u"create a Pubsub hook"), | 1899 help=_("create a Pubsub hook"), |
1909 ) | 1900 ) |
1910 self.need_loop = True | 1901 self.need_loop = True |
1911 | 1902 |
1912 def add_parser_options(self): | 1903 def add_parser_options(self): |
1913 self.parser.add_argument( | 1904 self.parser.add_argument( |
1914 "-t", | 1905 "-t", |
1915 "--type", | 1906 "--type", |
1916 default=u"python", | 1907 default="python", |
1917 choices=("python", "python_file", "python_code"), | 1908 choices=("python", "python_file", "python_code"), |
1918 help=_(u"hook type"), | 1909 help=_("hook type"), |
1919 ) | 1910 ) |
1920 self.parser.add_argument( | 1911 self.parser.add_argument( |
1921 "-P", | 1912 "-P", |
1922 "--persistent", | 1913 "--persistent", |
1923 action="store_true", | 1914 action="store_true", |
1924 help=_(u"make hook persistent across restarts"), | 1915 help=_("make hook persistent across restarts"), |
1925 ) | 1916 ) |
1926 self.parser.add_argument( | 1917 self.parser.add_argument( |
1927 "hook_arg", | 1918 "hook_arg", |
1928 type=base.unicode_decoder, | 1919 help=_("argument of the hook (depend of the type)"), |
1929 help=_(u"argument of the hook (depend of the type)"), | |
1930 ) | 1920 ) |
1931 | 1921 |
1932 @staticmethod | 1922 @staticmethod |
1933 def checkArgs(self): | 1923 def checkArgs(self): |
1934 if self.args.type == u"python_file": | 1924 if self.args.type == "python_file": |
1935 self.args.hook_arg = os.path.abspath(self.args.hook_arg) | 1925 self.args.hook_arg = os.path.abspath(self.args.hook_arg) |
1936 if not os.path.isfile(self.args.hook_arg): | 1926 if not os.path.isfile(self.args.hook_arg): |
1937 self.parser.error( | 1927 self.parser.error( |
1938 _(u"{path} is not a file").format(path=self.args.hook_arg) | 1928 _("{path} is not a file").format(path=self.args.hook_arg) |
1939 ) | 1929 ) |
1940 | 1930 |
1941 def start(self): | 1931 def start(self): |
1942 self.checkArgs(self) | 1932 self.checkArgs(self) |
1943 self.host.bridge.psHookAdd( | 1933 self.host.bridge.psHookAdd( |
1948 self.args.persistent, | 1938 self.args.persistent, |
1949 self.profile, | 1939 self.profile, |
1950 callback=self.host.quit, | 1940 callback=self.host.quit, |
1951 errback=partial( | 1941 errback=partial( |
1952 self.errback, | 1942 self.errback, |
1953 msg=_(u"can't create hook: {}"), | 1943 msg=_("can't create hook: {}"), |
1954 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1944 exit_code=C.EXIT_BRIDGE_ERRBACK, |
1955 ), | 1945 ), |
1956 ) | 1946 ) |
1957 | 1947 |
1958 | 1948 |
1962 self, | 1952 self, |
1963 host, | 1953 host, |
1964 "delete", | 1954 "delete", |
1965 use_pubsub=True, | 1955 use_pubsub=True, |
1966 pubsub_flags={C.NODE}, | 1956 pubsub_flags={C.NODE}, |
1967 help=_(u"delete a Pubsub hook"), | 1957 help=_("delete a Pubsub hook"), |
1968 ) | 1958 ) |
1969 self.need_loop = True | 1959 self.need_loop = True |
1970 | 1960 |
1971 def add_parser_options(self): | 1961 def add_parser_options(self): |
1972 self.parser.add_argument( | 1962 self.parser.add_argument( |
1973 "-t", | 1963 "-t", |
1974 "--type", | 1964 "--type", |
1975 default=u"", | 1965 default="", |
1976 choices=("", "python", "python_file", "python_code"), | 1966 choices=("", "python", "python_file", "python_code"), |
1977 help=_(u"hook type to remove, empty to remove all (DEFAULT: remove all)"), | 1967 help=_("hook type to remove, empty to remove all (DEFAULT: remove all)"), |
1978 ) | 1968 ) |
1979 self.parser.add_argument( | 1969 self.parser.add_argument( |
1980 "-a", | 1970 "-a", |
1981 "--arg", | 1971 "--arg", |
1982 dest="hook_arg", | 1972 dest="hook_arg", |
1983 type=base.unicode_decoder, | 1973 default="", |
1984 default=u"", | |
1985 help=_( | 1974 help=_( |
1986 u"argument of the hook to remove, empty to remove all (DEFAULT: remove all)" | 1975 "argument of the hook to remove, empty to remove all (DEFAULT: remove all)" |
1987 ), | 1976 ), |
1988 ) | 1977 ) |
1989 | 1978 |
1990 def psHookRemoveCb(self, nb_deleted): | 1979 def psHookRemoveCb(self, nb_deleted): |
1991 self.disp( | 1980 self.disp( |
1992 _(u"{nb_deleted} hook(s) have been deleted").format(nb_deleted=nb_deleted) | 1981 _("{nb_deleted} hook(s) have been deleted").format(nb_deleted=nb_deleted) |
1993 ) | 1982 ) |
1994 self.host.quit() | 1983 self.host.quit() |
1995 | 1984 |
1996 def start(self): | 1985 def start(self): |
1997 HookCreate.checkArgs(self) | 1986 HookCreate.checkArgs(self) |
2002 self.args.hook_arg, | 1991 self.args.hook_arg, |
2003 self.profile, | 1992 self.profile, |
2004 callback=self.psHookRemoveCb, | 1993 callback=self.psHookRemoveCb, |
2005 errback=partial( | 1994 errback=partial( |
2006 self.errback, | 1995 self.errback, |
2007 msg=_(u"can't delete hook: {}"), | 1996 msg=_("can't delete hook: {}"), |
2008 exit_code=C.EXIT_BRIDGE_ERRBACK, | 1997 exit_code=C.EXIT_BRIDGE_ERRBACK, |
2009 ), | 1998 ), |
2010 ) | 1999 ) |
2011 | 2000 |
2012 | 2001 |
2015 base.CommandBase.__init__( | 2004 base.CommandBase.__init__( |
2016 self, | 2005 self, |
2017 host, | 2006 host, |
2018 "list", | 2007 "list", |
2019 use_output=C.OUTPUT_LIST_DICT, | 2008 use_output=C.OUTPUT_LIST_DICT, |
2020 help=_(u"list hooks of a profile"), | 2009 help=_("list hooks of a profile"), |
2021 ) | 2010 ) |
2022 self.need_loop = True | 2011 self.need_loop = True |
2023 | 2012 |
2024 def add_parser_options(self): | 2013 def add_parser_options(self): |
2025 pass | 2014 pass |
2026 | 2015 |
2027 def psHookListCb(self, data): | 2016 def psHookListCb(self, data): |
2028 if not data: | 2017 if not data: |
2029 self.disp(_(u"No hook found.")) | 2018 self.disp(_("No hook found.")) |
2030 self.output(data) | 2019 self.output(data) |
2031 self.host.quit() | 2020 self.host.quit() |
2032 | 2021 |
2033 def start(self): | 2022 def start(self): |
2034 self.host.bridge.psHookList( | 2023 self.host.bridge.psHookList( |
2035 self.profile, | 2024 self.profile, |
2036 callback=self.psHookListCb, | 2025 callback=self.psHookListCb, |
2037 errback=partial( | 2026 errback=partial( |
2038 self.errback, | 2027 self.errback, |
2039 msg=_(u"can't list hooks: {}"), | 2028 msg=_("can't list hooks: {}"), |
2040 exit_code=C.EXIT_BRIDGE_ERRBACK, | 2029 exit_code=C.EXIT_BRIDGE_ERRBACK, |
2041 ), | 2030 ), |
2042 ) | 2031 ) |
2043 | 2032 |
2044 | 2033 |