summaryrefslogtreecommitdiff
path: root/src/obsync
diff options
context:
space:
mode:
authorColin Patrick McCabe <cmccabe@alumni.cmu.edu>2011-05-18 16:39:18 -0700
committerColin Patrick McCabe <cmccabe@alumni.cmu.edu>2011-05-25 10:21:47 -0700
commit42f873e6924484c6414bc56b62953876739458d1 (patch)
tree101c1816ee8e7cb63428a2966f2128db9c1b2293 /src/obsync
parentfe955881a835aca3a420ef7bcc17d44aa727749f (diff)
downloadceph-42f873e6924484c6414bc56b62953876739458d1.tar.gz
Proper ACL support for rados targets
Signed-off-by: Colin McCabe <colin.mccabe@dreamhost.com>
Diffstat (limited to 'src/obsync')
-rwxr-xr-xsrc/obsync/obsync109
1 files changed, 73 insertions, 36 deletions
diff --git a/src/obsync/obsync b/src/obsync/obsync
index 4d47e2260a8..f0a46fee1c5 100755
--- a/src/obsync/obsync
+++ b/src/obsync/obsync
@@ -44,12 +44,19 @@ global opts
# Translation table mapping users in the source to users in the destination.
global xuser
+# Librgw instance
+global rgw
+
###### Constants #######
-RGW_META_BUCKET_NAME = ".rgw"
ACL_XATTR = "rados.acl"
META_XATTR_PREFIX = "rados.meta."
CONTENT_TYPE_XATTR = "rados.content_type"
+RGW_META_BUCKET_NAME = ".rgw"
+RGW_META_ETAG = "user.rgw.etag"
+RGW_META_PREFIX = "user.x-amz-meta-"
+RGW_META_CONTENT_TYPE = "user.content_type"
+
###### Exception classes #######
class InvalidLocalName(Exception):
pass
@@ -433,7 +440,10 @@ class Store(object):
return S3Store(s3_url, create, akey, skey)
rados_url = strip_prefix("rados:", url)
if (rados_url):
- return RadosStore(rados_url, create, akey, skey)
+ dst_owner = None
+ if (create and os.environ.has_key("DST_OWNER")):
+ dst_owner = os.environ["DST_OWNER"]
+ return RadosStore(rados_url, create, akey, skey, dst_owner)
file_url = strip_prefix("file://", url)
if (file_url):
return FileStore(file_url, create)
@@ -672,11 +682,7 @@ class FileStoreIterator(object):
# Ignore non-files when iterating.
if (not os.path.isfile(path)):
continue
- try:
- obj_name = local_name_to_s3_name(path[len(self.base)+1:])
- except LocalFileIsAcl, e:
- # ignore ACL side files when iterating
- continue
+ obj_name = local_name_to_s3_name(path[len(self.base)+1:])
return Object.from_file(obj_name, path)
class FileStore(Store):
@@ -769,7 +775,10 @@ class RadosStoreIterator(object):
return ret
class RadosStore(Store):
- def __init__(self, url, create, akey, skey):
+ def __init__(self, url, create, akey, skey, owner):
+ if (rgw == None):
+ rgw = Rgw()
+ self.owner = owner
# Parse the rados url
conf_end = string.find(url, ":")
if (conf_end == -1):
@@ -790,19 +799,6 @@ rados:/path/to/ceph/conf:pool:key_prefix. Failed to find the bucket.")
print "self.conf_file_path = '" + self.conf_file_path + "', ",
print "self.rgw_bucket_name = '" + self.rgw_bucket_name + "' ",
print "self.key_prefix = '" + self.key_prefix + "'"
- acl_hack = getenv("ACL_HACK", None)
- if (acl_hack == None):
- raise Exception("RadosStore error: You must specify an environment " +
- "variable called ACL_HACK containing the name of a file. This " +
- "file contains a serialized RGW ACL that you want " +
- "to insert into the user.rgw.acl extended attribute of all " +
- "the objects you create. This is a hack and yes, it will go " +
- "away soon.")
- acl_hack_f = open(acl_hack, "r")
- try:
- self.acl_hack = acl_hack_f.read()
- finally:
- acl_hack_f.close()
self.rados = rados.Rados()
self.rados.conf_read_file(self.conf_file_path)
self.rados.connect()
@@ -811,36 +807,70 @@ rados:/path/to/ceph/conf:pool:key_prefix. Failed to find the bucket.")
self.create_rgw_bucket(self.rgw_bucket_name)
else:
raise NonexistentStore()
+ else:
+ # Figure out what owner we should use when creating objects.
+ # We use the owner of the destination bucket
+ bin_ = meta_ctx.get_xattr(self.rgw_bucket_name, "user.rgw.acl")
+ xml = rgw.acl_bin2xml(bin_)
+ acl = AclPolicy.from_xml(obj.name, xml)
+ self.bucket_owner = acl.owner_id
+ if (self.more_verbose):
+ print "using owner \"%s\"" % self.bucket_owner
self.ioctx = self.rados.open_ioctx(self.rgw_bucket_name)
Store.__init__(self, "rados:" + url)
def create_rgw_bucket(self, rgw_bucket_name):
""" Create an rgw bucket named 'rgw_bucket_name' """
+ if (self.bucket_owner == None):
+ raise Exception("Can't create a bucket without knowing who " +
+ "should own it. Please set DST_OWNER")
self.rados.create_pool(self.rgw_bucket_name)
meta_ctx = None
try:
meta_ctx = self.rados.open_ioctx(RGW_META_BUCKET_NAME)
meta_ctx.write(rgw_bucket_name, "", 0)
print "meta_ctx.set_xattr(rgw_bucket_name=" + rgw_bucket_name + ", " + \
- "user.rgw.acl, self.acl_hack=" + self.acl_hack + ")"
- meta_ctx.set_xattr(rgw_bucket_name, "user.rgw.acl", self.acl_hack)
+ "user.rgw.acl=" + self.acl_hack + ")"
+ new_bucket_acl = "\
+<AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> \
+<Owner><ID>%s</ID></Owner><AccessControlList>\
+<Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
+xsi:type=\"CanonicalUser\"><ID>%s</ID> \
+<DisplayName>display-name</DisplayName></Grantee> \
+<Permission>FULL_CONTROL</Permission></Grant>\
+</AccessControlList></AccessControlPolicy>" % (self.dst_owner, self.dst_owner)
+ new_bucket_acl_bin = rgw.acl_xml2bin(new_bucket_acl)
+ meta_ctx.set_xattr(rgw_bucket_name, "user.rgw.acl", new_bucket_acl_bin)
finally:
if (meta_ctx):
meta_ctx.close()
- def obsync_obj_from_rgw(self, key):
+ def obsync_obj_from_rgw(self, obj):
"""Create an obsync object from a Rados object"""
try:
- size, tm = self.ioctx.stat(key)
+ size, tm = self.ioctx.stat(obj)
except rados.ObjectNotFound:
return None
- md5 = self.ioctx.get_xattr(key, "user.rgw.etag")
- # TODO: support meta
- return Object(key, md5, size, {})
+ md5 = None
+ meta = {}
+ for k,v in self.ioctx.get_xattrs(obj):
+ if k == RGW_META_ETAG:
+ md5 = v
+ elif k == RGW_META_CONTENT_TYPE:
+ meta[CONTENT_TYPE_XATTR] = v
+ elif k[:RGW_META_PREFIX] == RGW_META_PREFIX:
+ meta["rados.meta." + k[RGW_META_PREFIX:]] = v
+ if (md5 == None):
+ raise RuntimeError("error on object %s: expected to find " + \
+ "extended attribute %s" % (obj, RGW_META_ETAG))
+ return Object(key, md5, size, meta)
def __str__(self):
return "rados:" + self.conf_file_path + ":" + self.rgw_bucket_name + ":" + self.key_prefix
def get_acl(self, obj):
- acl = LocalAcl(obj.name)
- # todo: set XML ACL
- return acl
+ try:
+ bin_ = self.ioctx.get_xattr(obj.name, ACL_XATTR)
+ except rados.ObjectNotFound:
+ return LocalAcl.get_empty(obj.name)
+ xml = rgw.acl_bin2xml(bin_)
+ return LocalAcl.from_xml(obj.name, xml)
def make_local_copy(self, obj):
temp_file = None
temp_file_f = None
@@ -857,7 +887,6 @@ rados:/path/to/ceph/conf:pool:key_prefix. Failed to find the bucket.")
break
off += 8192
temp_file_f.close()
- # TODO: implement ACLs
except:
if (temp_file_f):
temp_file_f.close()
@@ -886,11 +915,19 @@ rados:/path/to/ceph/conf:pool:key_prefix. Failed to find the bucket.")
if (len(buf) < 8192):
break
off += 8192
- # TODO: examine obj.meta
self.ioctx.set_xattr(obj.name, "user.rgw.etag", obj.md5)
- self.ioctx.set_xattr(obj.name, "user.rgw.acl", self.acl_hack)
- self.ioctx.set_xattr(obj.name, "user.rgw.content_type",
- "application/octet-stream")
+ if (src_acl.acl_policy != None):
+ xml = src_acl.acl_policy.to_xml()
+ bin_ = rgw.acl_xml2bin(xml)
+ self.ioctx.set_xattr(obj.name, "user.rgw.acl", bin_)
+ for k,v in obj.meta.items():
+ self.ioctx.set_xattr(obj.name,
+ RGW_META_PREFIX + k[META_XATTR_PREFIX:], v)
+ if (obj.meta.has_key(CONTENT_TYPE_XATTR)):
+ content_type = meta[CONTENT_TYPE_XATTR]
+ else:
+ content_type = "application/octet-stream"
+ self.ioctx.set_xattr(obj.name, "user.rgw.content_type", content_type)
def remove(self, obj):
if (opts.dry_run):
return