]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/cli.py
CLI: fix examples in tahoe put --help
[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
43         aliases = get_aliases(self['node-directory'])
44         if self['dir-cap']:
45             aliases["tahoe"] = self['dir-cap']
46         self.aliases = aliases # maps alias name to dircap
47
48
49 class MakeDirectoryOptions(VDriveOptions):
50     def parseArgs(self, where=""):
51         self.where = where
52     longdesc = """Create a new directory, either unlinked or as a subdirectory."""
53
54 class AddAliasOptions(VDriveOptions):
55     def parseArgs(self, alias, cap):
56         self.alias = alias
57         self.cap = cap
58
59 class CreateAliasOptions(VDriveOptions):
60     def parseArgs(self, alias):
61         self.alias = alias
62
63 class ListAliasOptions(VDriveOptions):
64     pass
65
66 class ListOptions(VDriveOptions):
67     optFlags = [
68         ("long", "l", "Use long format: show file sizes, and timestamps"),
69         ("uri", "u", "Show file/directory URIs"),
70         ("readonly-uri", None, "Show readonly file/directory URIs"),
71         ("classify", "F", "Append '/' to directory names, and '*' to mutable"),
72         ("json", None, "Show the raw JSON output"),
73         ]
74     def parseArgs(self, where=""):
75         self.where = where
76
77     longdesc = """List the contents of some portion of the virtual drive."""
78
79 class GetOptions(VDriveOptions):
80     def parseArgs(self, arg1, arg2=None):
81         # tahoe get FOO |less            # write to stdout
82         # tahoe get tahoe:FOO |less      # same
83         # tahoe get FOO bar              # write to local file
84         # tahoe get tahoe:FOO bar        # same
85
86         self.from_file = arg1
87         self.to_file = arg2
88         if self.to_file == "-":
89             self.to_file = None
90
91     def getSynopsis(self):
92         return "%s get VDRIVE_FILE LOCAL_FILE" % (os.path.basename(sys.argv[0]),)
93
94     longdesc = """Retrieve a file from the virtual drive and write it to the
95     local filesystem. If LOCAL_FILE is omitted or '-', the contents of the file
96     will be written to stdout."""
97
98     def getUsage(self, width=None):
99         t = VDriveOptions.getUsage(self, width)
100         t += """
101 Examples:
102  % tahoe get FOO |less            # write to stdout
103  % tahoe get tahoe:FOO |less      # same
104  % tahoe get FOO bar              # write to local file
105  % tahoe get tahoe:FOO bar        # same
106 """
107         return t
108
109 class PutOptions(VDriveOptions):
110     optFlags = [
111         ("mutable", "m", "Create a mutable file instead of an immutable one."),
112         ]
113
114     def parseArgs(self, arg1=None, arg2=None):
115         # cat FILE | tahoe put           # create unlinked file from stdin
116         # cat FILE | tahoe put -         # same
117         # tahoe put bar                  # create unlinked file from local 'bar'
118         # cat FILE | tahoe put - FOO     # create tahoe:FOO from stdin
119         # tahoe put bar FOO              # copy local 'bar' to tahoe:FOO
120         # tahoe put bar tahoe:FOO        # same
121
122         if arg1 is not None and arg2 is not None:
123             self.from_file = arg1
124             self.to_file = arg2
125         elif arg1 is not None and arg2 is None:
126             self.from_file = arg1 # might be "-"
127             self.to_file = None
128         else:
129             self.from_file = None
130             self.to_file = None
131         if self.from_file == "-":
132             self.from_file = None
133
134     def getSynopsis(self):
135         return "%s put LOCAL_FILE VDRIVE_FILE" % (os.path.basename(sys.argv[0]),)
136
137     longdesc = """Put a file into the virtual drive (copying the file's
138     contents from the local filesystem). If VDRIVE_FILE is missing, upload
139     the file but do not link it into a directory: prints the new filecap to
140     stdout. If LOCAL_FILE is missing or '-', data will be copied from stdin.
141     VDRIVE_FILE is assumed to start with tahoe: unless otherwise specified."""
142
143     def getUsage(self, width=None):
144         t = VDriveOptions.getUsage(self, width)
145         t += """
146 Examples:
147  % cat FILE | tahoe put                # create unlinked file from stdin
148  % cat FILE | tahoe -                  # same
149  % tahoe put bar                       # create unlinked file from local 'bar'
150  % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
151  % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
152  % tahoe put bar tahoe:FOO             # same
153  % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
154 """
155         return t
156
157 class CpOptions(VDriveOptions):
158     optFlags = [
159         ("recursive", "r", "Copy source directory recursively."),
160         ("verbose", "v", "Be noisy about what is happening."),
161         ]
162     def parseArgs(self, *args):
163         if len(args) < 2:
164             raise usage.UsageError("cp requires at least two arguments")
165         self.sources = args[:-1]
166         self.destination = args[-1]
167
168 class RmOptions(VDriveOptions):
169     def parseArgs(self, where):
170         self.where = where
171
172     def getSynopsis(self):
173         return "%s rm VE_FILE" % (os.path.basename(sys.argv[0]),)
174
175 class MvOptions(VDriveOptions):
176     def parseArgs(self, frompath, topath):
177         self.from_file = frompath
178         self.to_file = topath
179
180     def getSynopsis(self):
181         return "%s mv FROM TO" % (os.path.basename(sys.argv[0]),)
182
183 class LnOptions(VDriveOptions):
184     def parseArgs(self, frompath, topath):
185         self.from_file = frompath
186         self.to_file = topath
187
188     def getSynopsis(self):
189         return "%s ln FROM TO" % (os.path.basename(sys.argv[0]),)
190
191 class WebopenOptions(VDriveOptions):
192     def parseArgs(self, where=''):
193         self.where = where
194
195     def getSynopsis(self):
196         return "%s webopen [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
197
198     longdesc = """Opens a webbrowser to the contents of some portion of the virtual drive."""
199
200 class ManifestOptions(VDriveOptions):
201     optFlags = [
202         ("storage-index", "s", "Only print storage index strings, not pathname+cap"),
203         ("verify-cap", None, "Only print verifycap, not pathname+cap"),
204         ("repair-cap", None, "Only print repaircap, not pathname+cap"),
205         ("raw", "r", "Display raw JSON data instead of parsed"),
206         ]
207     def parseArgs(self, where=''):
208         self.where = where
209
210     def getSynopsis(self):
211         return "%s manifest [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
212
213     longdesc = """Print a list of all files/directories reachable from the given starting point."""
214
215 class StatsOptions(VDriveOptions):
216     optFlags = [
217         ("raw", "r", "Display raw JSON data instead of parsed"),
218         ]
219     def parseArgs(self, where=''):
220         self.where = where
221
222     def getSynopsis(self):
223         return "%s stats [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
224
225     longdesc = """Print statistics about of all files/directories reachable from the given starting point."""
226
227 class CheckOptions(VDriveOptions):
228     optFlags = [
229         ("raw", "r", "Display raw JSON data instead of parsed"),
230         ("verify", "v", "Verify all hashes, instead of merely querying share presence"),
231         ("repair", "r", "Automatically repair any problems found"),
232         ]
233     def parseArgs(self, where=''):
234         self.where = where
235
236     def getSynopsis(self):
237         return "%s check [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
238
239     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."""
240
241 class DeepCheckOptions(VDriveOptions):
242     optFlags = [
243         ("raw", "r", "Display raw JSON data instead of parsed"),
244         ("verify", "v", "Verify all hashes, instead of merely querying share presence"),
245         ("repair", "r", "Automatically repair any problems found"),
246         ]
247     def parseArgs(self, where=''):
248         self.where = where
249
250     def getSynopsis(self):
251         return "%s deep-check [ALIAS:PATH]" % (os.path.basename(sys.argv[0]),)
252
253     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."""
254
255 subCommands = [
256     ["mkdir", None, MakeDirectoryOptions, "Create a new directory"],
257     ["add-alias", None, AddAliasOptions, "Add a new alias cap"],
258     ["create-alias", None, CreateAliasOptions, "Create a new alias cap"],
259     ["list-aliases", None, ListAliasOptions, "List all alias caps"],
260     ["ls", None, ListOptions, "List a directory"],
261     ["get", None, GetOptions, "Retrieve a file from the virtual drive."],
262     ["put", None, PutOptions, "Upload a file into the virtual drive."],
263     ["cp", None, CpOptions, "Copy one or more files."],
264     ["rm", None, RmOptions, "Unlink a file or directory in the virtual drive."],
265     ["mv", None, MvOptions, "Move a file within the virtual drive."],
266     ["ln", None, LnOptions, "Make an additional link to an existing file."],
267     ["webopen", None, WebopenOptions, "Open a webbrowser to the root_dir"],
268     ["manifest", None, ManifestOptions, "List all files/dirs in a subtree"],
269     ["stats", None, StatsOptions, "Print statistics about all files/dirs in a subtree"],
270     ["check", None, CheckOptions, "Check a single file or directory"],
271     ["deep-check", None, DeepCheckOptions, "Check all files/directories reachable from a starting point"],
272     ]
273
274 def mkdir(options):
275     from allmydata.scripts import tahoe_mkdir
276     rc = tahoe_mkdir.mkdir(options)
277     return rc
278
279 def add_alias(options):
280     from allmydata.scripts import tahoe_add_alias
281     rc = tahoe_add_alias.add_alias(options)
282     return rc
283
284 def create_alias(options):
285     from allmydata.scripts import tahoe_add_alias
286     rc = tahoe_add_alias.create_alias(options)
287     return rc
288
289 def list_aliases(options):
290     from allmydata.scripts import tahoe_add_alias
291     rc = tahoe_add_alias.list_aliases(options)
292     return rc
293
294 def list(options):
295     from allmydata.scripts import tahoe_ls
296     rc = tahoe_ls.list(options)
297     return rc
298
299 def get(options):
300     from allmydata.scripts import tahoe_get
301     rc = tahoe_get.get(options)
302     if rc == 0:
303         if options.to_file is None:
304             # be quiet, since the file being written to stdout should be
305             # proof enough that it worked, unless the user is unlucky
306             # enough to have picked an empty file
307             pass
308         else:
309             print >>options.stderr, "%s retrieved and written to %s" % \
310                   (options.from_file, options.to_file)
311     return rc
312
313 def put(options):
314     from allmydata.scripts import tahoe_put
315     rc = tahoe_put.put(options)
316     return rc
317
318 def cp(options):
319     from allmydata.scripts import tahoe_cp
320     rc = tahoe_cp.copy(options)
321     return rc
322
323 def rm(options):
324     from allmydata.scripts import tahoe_rm
325     rc = tahoe_rm.rm(options)
326     return rc
327
328 def mv(options):
329     from allmydata.scripts import tahoe_mv
330     rc = tahoe_mv.mv(options, mode="move")
331     return rc
332
333 def ln(options):
334     from allmydata.scripts import tahoe_mv
335     rc = tahoe_mv.mv(options, mode="link")
336     return rc
337
338 def webopen(options, opener=None):
339     from allmydata.scripts import tahoe_webopen
340     rc = tahoe_webopen.webopen(options, opener=opener)
341     return rc
342
343 def manifest(options):
344     from allmydata.scripts import tahoe_manifest
345     rc = tahoe_manifest.manifest(options)
346     return rc
347
348 def stats(options):
349     from allmydata.scripts import tahoe_manifest
350     rc = tahoe_manifest.stats(options)
351     return rc
352
353 def check(options):
354     from allmydata.scripts import tahoe_check
355     rc = tahoe_check.check(options)
356     return rc
357
358 def deepcheck(options):
359     from allmydata.scripts import tahoe_check
360     rc = tahoe_check.deepcheck(options)
361     return rc
362
363 dispatch = {
364     "mkdir": mkdir,
365     "add-alias": add_alias,
366     "create-alias": create_alias,
367     "list-aliases": list_aliases,
368     "ls": list,
369     "get": get,
370     "put": put,
371     "cp": cp,
372     "rm": rm,
373     "mv": mv,
374     "ln": ln,
375     "webopen": webopen,
376     "manifest": manifest,
377     "stats": stats,
378     "check": check,
379     "deep-check": deepcheck,
380     }
381