From 4361b32f2d66a2d5c862b910c549903ff20d8469 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Thu, 11 Oct 2007 20:31:48 -0700 Subject: [PATCH] cli: implement 'mv'. Closes #162. --- src/allmydata/scripts/cli.py | 21 ++++++++++ src/allmydata/scripts/tahoe_mv.py | 69 +++++++++++++++++++++++++++++++ src/allmydata/test/test_system.py | 28 +++++++++++-- 3 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/allmydata/scripts/tahoe_mv.py diff --git a/src/allmydata/scripts/cli.py b/src/allmydata/scripts/cli.py index 99e2fa47..a0a674c7 100644 --- a/src/allmydata/scripts/cli.py +++ b/src/allmydata/scripts/cli.py @@ -94,12 +94,21 @@ class RmOptions(VDriveOptions): def getSynopsis(self): return "%s rm VE_FILE" % (os.path.basename(sys.argv[0]),) +class MvOptions(VDriveOptions): + def parseArgs(self, frompath, topath): + self['from'] = frompath + self['to'] = topath + + def getSynopsis(self): + return "%s mv FROM TO" % (os.path.basename(sys.argv[0]),) + subCommands = [ ["ls", None, ListOptions, "List a directory"], ["get", None, GetOptions, "Retrieve a file from the virtual drive."], ["put", None, PutOptions, "Upload a file into the virtual drive."], ["rm", None, RmOptions, "Unlink a file or directory in the virtual drive."], + ["mv", None, MvOptions, "Move a file within the virtual drive."], ] def list(config, stdout, stderr): @@ -160,10 +169,22 @@ def rm(config, stdout, stderr): stdout, stderr) return rc +def mv(config, stdout, stderr): + from allmydata.scripts import tahoe_mv + frompath = config['from'] + topath = config['to'] + rc = tahoe_mv.mv(config['node-url'], + config['root-uri'], + frompath, + topath, + stdout, stderr) + return rc + dispatch = { "ls": list, "get": get, "put": put, "rm": rm, + "mv": mv, } diff --git a/src/allmydata/scripts/tahoe_mv.py b/src/allmydata/scripts/tahoe_mv.py new file mode 100644 index 00000000..4020c6e8 --- /dev/null +++ b/src/allmydata/scripts/tahoe_mv.py @@ -0,0 +1,69 @@ +#! /usr/bin/python + +import re +import urllib, httplib +import urlparse +import simplejson + +# copied from twisted/web/client.py +def _parse(url, defaultPort=None): + url = url.strip() + parsed = urlparse.urlparse(url) + scheme = parsed[0] + path = urlparse.urlunparse(('','')+parsed[2:]) + if defaultPort is None: + if scheme == 'https': + defaultPort = 443 + else: + defaultPort = 80 + host, port = parsed[1], defaultPort + if ':' in host: + host, port = host.split(':') + port = int(port) + if path == "": + path = "/" + return scheme, host, port, path + +def do_http(method, url, body=""): + scheme, host, port, path = _parse(url) + if scheme == "http": + c = httplib.HTTPConnection(host, port) + elif scheme == "https": + c = httplib.HTTPSConnection(host, port) + else: + raise ValueError("unknown scheme '%s', need http or https" % scheme) + c.putrequest(method, path) + import allmydata + c.putheader("User-Agent", "tahoe_mv/%s" % allmydata.__version__) + c.putheader("Content-Length", str(len(body))) + c.endheaders() + c.send(body) + return c.getresponse() + +def mv(nodeurl, root_uri, frompath, topath, stdout, stderr): + if nodeurl[-1] != "/": + nodeurl += "/" + url = nodeurl + "uri/%s/" % urllib.quote(root_uri.replace("/","!")) + data = urllib.urlopen(url + frompath + "?t=json").read() + + nodetype, attrs = simplejson.loads(data) + uri = attrs.get("rw_uri") or attrs["ro_uri"] + + put_url = url + topath + "?t=uri" + resp = do_http("PUT", put_url, uri) + status = resp.status + if not re.search(r'^2\d\d$', str(status)): + print >>stderr, "error, got %s %s" % (resp.status, resp.reason) + print >>stderr, resp.read() + + # now remove the original + resp = do_http("DELETE", url + frompath) + if not re.search(r'^2\d\d$', str(status)): + print >>stderr, "error, got %s %s" % (resp.status, resp.reason) + print >>stderr, resp.read() + + print >>stdout, "OK" + return + + + diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py index 649dc04f..6b4c28d8 100644 --- a/src/allmydata/test/test_system.py +++ b/src/allmydata/test/test_system.py @@ -741,8 +741,30 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase): self.failUnlessEqual(err, "") d.addCallback(_check_get) + def _mv(res): + argv = ["mv"] + nodeargs + ["test_put/upload.txt", + "test_put/moved.txt"] + return self._run_cli(argv) + d.addCallback(_mv) + def _check_mv((out,err)): + self.failUnless("OK" in out) + self.failUnlessEqual(err, "") + vdrive0 = self.clients[0].getServiceNamed("vdrive") + d = defer.maybeDeferred(vdrive0.get_node_at_path, + "~/test_put/upload.txt") + d.addBoth(self.shouldFail, KeyError, "test_cli._check_rm", + "unable to find child named 'upload.txt'") + d.addCallback(lambda res: + vdrive0.get_node_at_path("~/test_put/moved.txt")) + d.addCallback(lambda filenode: filenode.download_to_data()) + def _check_mv2(res): + self.failUnless("I will not write" in res) + d.addCallback(_check_mv2) + return d + d.addCallback(_check_mv) + def _rm(res): - argv = ["rm"] + nodeargs + ["test_put/upload.txt"] + argv = ["rm"] + nodeargs + ["test_put/moved.txt"] return self._run_cli(argv) d.addCallback(_rm) def _check_rm((out,err)): @@ -750,9 +772,9 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase): self.failUnlessEqual(err, "") vdrive0 = self.clients[0].getServiceNamed("vdrive") d = defer.maybeDeferred(vdrive0.get_node_at_path, - "~/test_put/upload.txt") + "~/test_put/moved.txt") d.addBoth(self.shouldFail, KeyError, "test_cli._check_rm", - "unable to find child named 'upload.txt'") + "unable to find child named 'moved.txt'") return d d.addCallback(_check_rm) return d -- 2.45.2