]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/cli.py
Add missing synopsis and descriptions for alias commands.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / cli.py
1
2 import os.path, re, sys
3 from twisted.python import usage
4 from allmydata.scripts.common import BaseOptions, get_aliases
5
6 NODEURL_RE=re.compile("http://([^:]*)(:([1-9][0-9]*))?")
7
8 class VDriveOptions(BaseOptions, usage.Options):
9     optParameters = [
10         ["node-directory", "d", "~/.tahoe",
11          "Look here to find out which Tahoe node should be used for all "
12          "operations. The directory should either contain a full Tahoe node, "
13          "or a file named node.url which points to some other Tahoe node. "
14          "It should also contain a file named private/aliases which contains "
15          "the mapping from alias name to root dirnode URI."
16          ],
17         ["node-url", "u", None,
18          "URL of the tahoe node to use, a URL like \"http://127.0.0.1:3456\". "
19          "This overrides the URL found in the --node-directory ."],
20         ["dir-cap", None, None,
21          "Which dirnode URI should be used as the 'tahoe' alias."]
22         ]
23
24     def postOptions(self):
25         # compute a node-url from the existing options, put in self['node-url']
26         if self['node-directory']:
27             if sys.platform == 'win32' and self['node-directory'] == '~/.tahoe':
28                 from allmydata.windows import registry
29                 self['node-directory'] = registry.get_base_dir_path()
30             else:
31                 self['node-directory'] = os.path.expanduser(self['node-directory'])
32         if self['node-url']:
33             if (not isinstance(self['node-url'], basestring)
34                 or not NODEURL_RE.match(self['node-url'])):
35                 msg = ("--node-url is required to be a string and look like "
36                        "\"http://HOSTNAMEORADDR:PORT\", not: %r" %
37                        (self['node-url'],))
38                 raise usage.UsageError(msg)
39         else:
40             node_url_file = os.path.join(self['node-directory'], "node.url")
41             self['node-url'] = open(node_url_file, "r").read().strip()
42         if self['node-url'][-1] != "/":
43             self['node-url'] += "/"
44
45         aliases = get_aliases(self['node-directory'])
46         if self['dir-cap']:
47             aliases["tahoe"] = self['dir-cap']
48         self.aliases = aliases # maps alias name to dircap
49
50
51 class MakeDirectoryOptions(VDriveOptions):
52     def parseArgs(self, where=""):
53         self.where = where
54     longdesc = """Create a new directory, either unlinked or as a subdirectory."""
55
56 class AddAliasOptions(VDriveOptions):
57     def parseArgs(self, alias, cap):
58         self.alias = alias
59         self.cap = cap
60
61     def getSynopsis(self):
62         return "%s add-alias ALIAS DIRCAP" % (os.path.basename(sys.argv[0]),)
63
64     longdesc = """Add a new alias for an existing directory."""
65
66 class CreateAliasOptions(VDriveOptions):
67     def parseArgs(self, alias):
68         self.alias = alias
69
70     def getSynopsis(self):
71         return "%s create-alias ALIAS" % (os.path.basename(sys.argv[0]),)
72
73     longdesc = """Creates a new directory and adds an alias for it."""
74
75 class ListAliasOptions(VDriveOptions):
76     longdesc = """Displays a table of all configured aliases."""
77
78 class ListOptions(VDriveOptions):
79     optFlags = [
80         ("long", "l", "Use long format: show file sizes, and timestamps"),
81         ("uri", "u", "Show file/directory URIs"),
82         ("readonly-uri", None, "Show readonly file/directory URIs"),
83         ("classify", "F", "Append '/' to directory names, and '*' to mutable"),
84         ("json", None, "Show the raw JSON output"),
85         ]
86     def parseArgs(self, where=""):
87         self.where = where
88
89     longdesc = """List the contents of some portion of the virtual drive."""
90
91 class GetOptions(VDriveOptions):
92     def parseArgs(self, arg1, arg2=None):
93         # tahoe get FOO |less            # write to stdout
94         # tahoe get tahoe:FOO |less      # same
95         # tahoe get FOO bar              # write to local file
96         # tahoe get tahoe:FOO bar        # same
97
98         self.from_file = arg1
99         self.to_file = arg2
100         if self.to_file == "-":
101             self.to_file = None
102
103     def getSynopsis(self):
104         return "%s get VDRIVE_FILE LOCAL_FILE" % (os.path.basename(sys.argv[0]),)
105
106     longdesc = """Retrieve a file from the virtual drive and write it to the
107     local filesystem. If LOCAL_FILE is omitted or '-', the contents of the file
108     will be written to stdout."""
109
110     def getUsage(self, width=None):
111         t = VDriveOptions.getUsage(self, width)
112         t += """
113 Examples:
114  % tahoe get FOO |less            # write to stdout
115  % tahoe get tahoe:FOO |less      # same
116  % tahoe get FOO bar              # write to local file
117  % tahoe get tahoe:FOO bar        # same
118 """
119         return t
120
121 class PutOptions(VDriveOptions):
122     optFlags = [
123         ("mutable", "m", "Create a mutable file instead of an immutable one."),
124         ]
125
126     def parseArgs(self, arg1=None, arg2=None):
127         # cat FILE | tahoe put           # create unlinked file from stdin
128         # cat FILE | tahoe put -         # same
129         # tahoe put bar                  # create unlinked file from local 'bar'
130         # cat FILE | tahoe put - FOO     # create tahoe:FOO from stdin
131         # tahoe put bar FOO              # copy local 'bar' to tahoe:FOO
132         # tahoe put bar tahoe:FOO        # same
133
134         if arg1 is not None and arg2 is not None:
135             self.from_file = arg1
136             self.to_file = arg2
137         elif arg1 is not None and arg2 is None:
138             self.from_file = arg1 # might be "-"
139             self.to_file = None
140         else:
141             self.from_file = None
142             self.to_file = None
143         if self.from_file == "-":
144             self.from_file = None
145
146     def getSynopsis(self):
147         return "%s put LOCAL_FILE VDRIVE_FILE" % (os.path.basename(sys.argv[0]),)
148
149     longdesc = """Put a file into the virtual drive (copying the file's
150     contents from the local filesystem). If VDRIVE_FILE is missing, upload
151     the file but do not link it into a directory: prints the new filecap to
152     stdout. If LOCAL_FILE is missing or '-', data will be copied from stdin.
153     VDRIVE_FILE is assumed to start with tahoe: unless otherwise specified."""
154
155     def getUsage(self, width=None):
156         t = VDriveOptions.getUsage(self, width)
157         t += """
158 Examples:
159  % cat FILE | tahoe put                # create unlinked file from stdin
160  % cat FILE | tahoe -                  # same
161  % tahoe put bar                       # create unlinked file from local 'bar'
162  % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
163  % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
164  % tahoe put bar tahoe:FOO             # same
165  % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
166 """
167         return t
168
169 class CpOptions(VDriveOptions):
170     optFlags = [
171         ("recursive", "r", "Copy source directory recursively."),
172         ("verbose", "v", "Be noisy about what is happening."),
173         ]
174     def parseArgs(self, *args):
175         if len(args) < 2:
176             raise usage.UsageError("cp requires at least two arguments")
177         self.sources = args[:-1]
178         self.destination = args[-1]
179
180 class RmOptions(VDriveOptions):
181     def parseArgs(self, where):
182         self.where = where
183
184     def getSynopsis(self):
185         return "%s rm VDRIVE_FILE" % (os.path.basename(sys.argv[0]),)
186
187 class MvOptions(VDriveOptions):
188     def parseArgs(self, frompath, topath):
189         self.from_file = frompath
190         self.to_file = topath
191
192     def getSynopsis(self):
193         return "%s mv FROM TO" % (os.path.basename(sys.argv[0]),)
194
195 class LnOptions(VDriveOptions):
196     def parseArgs(self, frompath, topath):
197         self.from_file = frompath
198         self.to_file = topath
199
200     def getSynopsis(self):
201         return "%s ln FROM TO" % (os.path.basename(sys.argv[0]),)
202
203 class BackupOptions(VDriveOptions):
204     optFlags = [
205         ("verbose", "v", "Be noisy about what is happening."),
206         ("no-backupdb", None, "Do not use the SQLite-based backup-database (always upload all files)."),
207         ("ignore-timestamps", None, "Do not use backupdb timestamps to decide if a local file is unchanged."),
208         ]
209
210     def parseArgs(self, localdir, topath):
211         self.from_dir = localdir
212         self.to_dir = topath
213
214     def getSynopsis(Self):
215         return "%s backup FROM ALIAS:TO" % os.path.basename(sys.argv[0])
216
217     longdesc = """Add a versioned backup of the local FROM directory to a timestamped subdir of the (tahoe) TO/Archives directory, sharing as many files and directories as possible with the previous backup. Creates TO/Latest as a reference to the latest backup. Behaves somewhat like 'rsync -a --link-dest=TO/Archives/(previous) FROM TO/Archives/(new); ln -sf TO/Archives/(new) TO/Latest'."""
218
219 class WebopenOptions(VDriveOptions):
220     def parseArgs(self, where=''):
221         self.where = where
222
223     def getSynopsis(self):
224         return "%s webopen [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
225
226     longdesc = """Opens a webbrowser to the contents of some portion of the virtual drive."""
227
228 class ManifestOptions(VDriveOptions):
229     optFlags = [
230         ("storage-index", "s", "Only print storage index strings, not pathname+cap"),
231         ("verify-cap", None, "Only print verifycap, not pathname+cap"),
232         ("repair-cap", None, "Only print repaircap, not pathname+cap"),
233         ("raw", "r", "Display raw JSON data instead of parsed"),
234         ]
235     def parseArgs(self, where=''):
236         self.where = where
237
238     def getSynopsis(self):
239         return "%s manifest [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
240
241     longdesc = """Print a list of all files/directories reachable from the given starting point."""
242
243 class StatsOptions(VDriveOptions):
244     optFlags = [
245         ("raw", "r", "Display raw JSON data instead of parsed"),
246         ]
247     def parseArgs(self, where=''):
248         self.where = where
249
250     def getSynopsis(self):
251         return "%s stats [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
252
253     longdesc = """Print statistics about of all files/directories reachable from the given starting point."""
254
255 class CheckOptions(VDriveOptions):
256     optFlags = [
257         ("raw", None, "Display raw JSON data instead of parsed"),
258         ("verify", None, "Verify all hashes, instead of merely querying share presence"),
259         ("repair", None, "Automatically repair any problems found"),
260         ("add-lease", None, "Add/renew lease on all shares"),
261         ]
262     def parseArgs(self, where=''):
263         self.where = where
264
265     def getSynopsis(self):
266         return "%s check [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
267
268     longdesc = """Check a single file or directory: count how many shares are available, verify their hashes. Optionally repair the file if any problems were found."""
269
270 class DeepCheckOptions(VDriveOptions):
271     optFlags = [
272         ("raw", None, "Display raw JSON data instead of parsed"),
273         ("verify", None, "Verify all hashes, instead of merely querying share presence"),
274         ("repair", None, "Automatically repair any problems found"),
275         ("add-lease", None, "Add/renew lease on all shares"),
276         ("verbose", "v", "Be noisy about what is happening."),
277         ]
278     def parseArgs(self, where=''):
279         self.where = where
280
281     def getSynopsis(self):
282         return "%s deep-check [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
283
284     longdesc = """Check all files/directories reachable from the given starting point (which must be a directory), like 'tahoe check' but for multiple files. Optionally repair any problems found."""
285
286 subCommands = [
287     ["mkdir", None, MakeDirectoryOptions, "Create a new directory"],
288     ["add-alias", None, AddAliasOptions, "Add a new alias cap"],
289     ["create-alias", None, CreateAliasOptions, "Create a new alias cap"],
290     ["list-aliases", None, ListAliasOptions, "List all alias caps"],
291     ["ls", None, ListOptions, "List a directory"],
292     ["get", None, GetOptions, "Retrieve a file from the virtual drive."],
293     ["put", None, PutOptions, "Upload a file into the virtual drive."],
294     ["cp", None, CpOptions, "Copy one or more files."],
295     ["rm", None, RmOptions, "Unlink a file or directory in the virtual drive."],
296     ["mv", None, MvOptions, "Move a file within the virtual drive."],
297     ["ln", None, LnOptions, "Make an additional link to an existing file."],
298     ["backup", None, BackupOptions, "Make target dir look like local dir."],
299     ["webopen", None, WebopenOptions, "Open a webbrowser to the root_dir"],
300     ["manifest", None, ManifestOptions, "List all files/dirs in a subtree"],
301     ["stats", None, StatsOptions, "Print statistics about all files/dirs in a subtree"],
302     ["check", None, CheckOptions, "Check a single file or directory"],
303     ["deep-check", None, DeepCheckOptions, "Check all files/directories reachable from a starting point"],
304     ]
305
306 def mkdir(options):
307     from allmydata.scripts import tahoe_mkdir
308     rc = tahoe_mkdir.mkdir(options)
309     return rc
310
311 def add_alias(options):
312     from allmydata.scripts import tahoe_add_alias
313     rc = tahoe_add_alias.add_alias(options)
314     return rc
315
316 def create_alias(options):
317     from allmydata.scripts import tahoe_add_alias
318     rc = tahoe_add_alias.create_alias(options)
319     return rc
320
321 def list_aliases(options):
322     from allmydata.scripts import tahoe_add_alias
323     rc = tahoe_add_alias.list_aliases(options)
324     return rc
325
326 def list(options):
327     from allmydata.scripts import tahoe_ls
328     rc = tahoe_ls.list(options)
329     return rc
330
331 def get(options):
332     from allmydata.scripts import tahoe_get
333     rc = tahoe_get.get(options)
334     if rc == 0:
335         if options.to_file is None:
336             # be quiet, since the file being written to stdout should be
337             # proof enough that it worked, unless the user is unlucky
338             # enough to have picked an empty file
339             pass
340         else:
341             print >>options.stderr, "%s retrieved and written to %s" % \
342                   (options.from_file, options.to_file)
343     return rc
344
345 def put(options):
346     from allmydata.scripts import tahoe_put
347     rc = tahoe_put.put(options)
348     return rc
349
350 def cp(options):
351     from allmydata.scripts import tahoe_cp
352     rc = tahoe_cp.copy(options)
353     return rc
354
355 def rm(options):
356     from allmydata.scripts import tahoe_rm
357     rc = tahoe_rm.rm(options)
358     return rc
359
360 def mv(options):
361     from allmydata.scripts import tahoe_mv
362     rc = tahoe_mv.mv(options, mode="move")
363     return rc
364
365 def ln(options):
366     from allmydata.scripts import tahoe_mv
367     rc = tahoe_mv.mv(options, mode="link")
368     return rc
369
370 def backup(options):
371     from allmydata.scripts import tahoe_backup
372     rc = tahoe_backup.backup(options)
373     return rc
374
375 def webopen(options, opener=None):
376     from allmydata.scripts import tahoe_webopen
377     rc = tahoe_webopen.webopen(options, opener=opener)
378     return rc
379
380 def manifest(options):
381     from allmydata.scripts import tahoe_manifest
382     rc = tahoe_manifest.manifest(options)
383     return rc
384
385 def stats(options):
386     from allmydata.scripts import tahoe_manifest
387     rc = tahoe_manifest.stats(options)
388     return rc
389
390 def check(options):
391     from allmydata.scripts import tahoe_check
392     rc = tahoe_check.check(options)
393     return rc
394
395 def deepcheck(options):
396     from allmydata.scripts import tahoe_check
397     rc = tahoe_check.deepcheck(options)
398     return rc
399
400 dispatch = {
401     "mkdir": mkdir,
402     "add-alias": add_alias,
403     "create-alias": create_alias,
404     "list-aliases": list_aliases,
405     "ls": list,
406     "get": get,
407     "put": put,
408     "cp": cp,
409     "rm": rm,
410     "mv": mv,
411     "ln": ln,
412     "backup": backup,
413     "webopen": webopen,
414     "manifest": manifest,
415     "stats": stats,
416     "check": check,
417     "deep-check": deepcheck,
418     }
419