1. 找出对应bucket的owner
# radosgw-admin bucket list
# radosgw-admin bucket list --uid=ivan
[
"ivan_backup",
"ivan_bucket"
]
# radosgw-admin metadata list user
# radosgw-admin bucket stats --bucket=test_bucket
{
"bucket": "test_bucket",
"pool": "default.rgw.buckets.data",
"index_pool": "default.rgw.buckets.index",
"id": "5c470d18-0d9e-4a34-8a6c-7a6d64784c3e.38311.41",
"marker": "5c470d18-0d9e-4a34-8a6c-7a6d64784c3e.38311.41",
"owner": "FFF65F671D9E48F696C5E57931A1DE85",
"ver": "0#1",
"master_ver": "0#0",
"mtime": "2018-05-03 17:47:31.329045",
"max_marker": "0#",
"usage": {},
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
}
}
# radosgw-admin user info --uid=FFF65F671D9E48F696C5E57931A1DE85
{
"user_id": "FFF65F671D9E48F696C5E57931A1DE85",
"display_name": "user name",
"email": "",
"suspended": 0,
"max_buckets": 1000,
"auid": 0,
"subusers": [],
"keys": [
{
"user": "FFF65F671D9E48F696C5E57931A1DE85",
"access_key": "A5DXX3XCFX7OF4ZM1UY5",
"secret_key": "r1eeS2Sq5fuQF8JbOxjlauNDkPV7apZ6sArhFlUq"
}
],
"swift_keys": [],
"caps": [],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"temp_url_keys": []
}
# radosgw-admin user info --uid=admin
{
"user_id": "admin",
"display_name": "admin",
"email": "",
"suspended": 0,
"max_buckets": 1000,
"auid": 0,
"subusers": [],
"keys": [
{
"user": "admin",
"access_key": "HWG19J1AMB2MXU1YD0FW",
"secret_key": "vDahq8wV7MZPOhbLzQKuQ2cXldH2MmXeA89nIXZN"
}
],
"swift_keys": [],
"caps": [
{
"type": "buckets",
"perm": "*"
},
{
"type": "metadata",
"perm": "*"
},
{
"type": "usage",
"perm": "*"
},
{
"type": "users",
"perm": "*"
},
{
"type": "zone",
"perm": "*"
}
],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"temp_url_keys": []
}
2. 删除bucket
# radosgw-admin bucket rm --bucket=ivan_bucket --purge-objects
# radosgw-admin bucket rm --bucket=ivan_backup --purge-objects
3. bucket object管理
# radosgw-admin bucket list
[
"testab"
]
# radosgw-admin bucket list --bucket=testab
[
{
"name": "testobj.2~s7xdt9V5PGMqRXpd__fCQ06_Z_rTRTB.meta",
"instance": "",
"namespace": "multipart",
"owner": "0B3E5561D9D146B9BA07A73D5B08B61A",
"owner_display_name": "app_0B3E5561D9D146B9BA07A73D5B08B61A",
"size": 0,
"mtime": "2018-05-24 02:44:46.295558Z",
"etag": "",
"content_type": "binary\/octet-stream",
"tag": "_vp71VTOQeUYHEmtXF_75Pr6gYBcJHQn",
"flags": 0,
"user_data": ""
},
{
"name": "helloworld_1",
"instance": "",
"namespace": "",
"owner": "0B3E5561D9D146B9BA07A73D5B08B61A",
"owner_display_name": "app_0B3E5561D9D146B9BA07A73D5B08B61A",
"size": 129,
"mtime": "2018-05-24 07:36:37.854243Z",
"etag": "a2922a8dd298d6cc615cbbe25628df6e",
"content_type": "binary\/octet-stream",
"tag": "78b9bcbb-b328-4e4a-8417-4ed933be88bb.70594.16",
"flags": 0,
"user_data": ""
}
]
//删除bucket object
# radosgw-admin object rm --bucket=testab --object=helloworld_1
radosgw-admin object stat --bucket=jyeammolddraw --object=JH014121_v1.0.x_t
{
"name": "JH014121_v1.0.x_t",
"size": 9134263,
"policy": {
"acl": {
"acl_user_map": [
{
"user": "0A0B72E2B9374E148DA9D8486B975D2C",
"acl": 15
}
],
"acl_group_map": [],
"grant_map": [
{
"id": "0A0B72E2B9374E148DA9D8486B975D2C",
"grant": {
"type": {
"type": 0
},
"id": "0A0B72E2B9374E148DA9D8486B975D2C",
"email": "",
"permission": {
"flags": 15
},
"name": "app_0A0B72E2B9374E148DA9D8486B975D2C",
"group": 0
}
}
]
},
"owner": {
"id": "0A0B72E2B9374E148DA9D8486B975D2C",
"display_name": "app_0A0B72E2B9374E148DA9D8486B975D2C"
}
},
"etag": "45fcbd2bb460b40bc5dbcd226c51b749-18\u0000",
"tag": "135882fc-2865-43ab-9f71-7dd4b2095406.20040920.653647\u0000",
"manifest": {
"objs": [],
"obj_size": 9134263,
"explicit_objs": "false",
"head_obj": {
"bucket": {
"name": "jyeammolddraw",
"pool": "nanhai-01.rgw.buckets.data",
"data_extra_pool": "nanhai-01.rgw.buckets.non-ec",
"index_pool": "nanhai-01.rgw.buckets.index",
"marker": "135882fc-2865-43ab-9f71-7dd4b2095406.20035088.1163",
"bucket_id": "135882fc-2865-43ab-9f71-7dd4b2095406.20035088.1163",
"tenant": ""
},
"key": "",
"ns": "",
"object": "JH014121_v1.0.x_t",
"instance": "",
"orig_obj": "JH014121_v1.0.x_t"
},
"head_size": 0,
"max_head_size": 0,
"prefix": "JH014121_v1.0.x_t.2~f0A3ei1JrJxM381Q4Rg2YVmraISjSBC",
"tail_bucket": {
"name": "jyeammolddraw",
"pool": "nanhai-01.rgw.buckets.data",
"data_extra_pool": "nanhai-01.rgw.buckets.non-ec",
"index_pool": "nanhai-01.rgw.buckets.index",
"marker": "135882fc-2865-43ab-9f71-7dd4b2095406.20035088.1163",
"bucket_id": "135882fc-2865-43ab-9f71-7dd4b2095406.20035088.1163",
"tenant": ""
},
"rules": [
{
"key": 0,
"val": {
"start_part_num": 1,
"start_ofs": 0,
"part_size": 524288,
"stripe_max_size": 4194304,
"override_prefix": ""
}
},
{
"key": 8912896,
"val": {
"start_part_num": 18,
"start_ofs": 8912896,
"part_size": 221367,
"stripe_max_size": 4194304,
"override_prefix": ""
}
}
],
"tail_instance": ""
},
"attrs": {
"user.rgw.content_type": "binary\/octet-stream\u0000",
"user.rgw.pg_ver": "p>\u0000\u0000\u0000\u0000\u0000\u0000",
"user.rgw.source_zone": "\u000ei\u0010
"user.rgw.x-amz-acl": "private\u0000"
}
}
3. 用户管理
//1)创建s3用户
# radosgw-admin user create --uid='testuser' --display-name="testuser" --key-type=s3
{
"user_id": "testuser",
"display_name": "testuser",
"email": "",
"suspended": 0,
"max_buckets": 1000,
"auid": 0,
"subusers": [],
"keys": [
{
"user": "testuser",
"access_key": "N7S1NKVRU3ZCX1I06QGE",
"secret_key": "j5Zc1qFISWmWqenwRVeMBQCh8kVB8bAhj9q5PLus"
}
],
"swift_keys": [],
"caps": [],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"temp_url_keys": []
}
//2) 创建swift用户key
# radosgw-admin subuser create --subuser=testuser:swift --key-type=swift
{
"user_id": "testuser",
"display_name": "testuser",
"email": "",
"suspended": 0,
"max_buckets": 1000,
"auid": 0,
"subusers": [
{
"id": "testuser:swift",
"permissions": "<none>"
}
],
"keys": [
{
"user": "testuser",
"access_key": "N7S1NKVRU3ZCX1I06QGE",
"secret_key": "j5Zc1qFISWmWqenwRVeMBQCh8kVB8bAhj9q5PLus"
}
],
"swift_keys": [
{
"user": "testuser:swift",
"secret_key": "QosX7LgFtkvPj6eBDeOs0SQdP43MP3gatYA5SdoS"
}
],
"caps": [],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"temp_url_keys": []
}
//3) 删除s3用户
# radosgw-admin user rm --uid='testuser' --access-key=N7S1NKVRU3ZCX1I06QGE
//4) 列出所有的用户
# radosgw-admin metadata list user
//5) 删除用户及其对应的bucket数据
# radosgw-admin user rm --uid="909A284903754DAD910C68F9AFA2BE81" --purge-data
4. rgw配额管理
# radosgw-admin user info --uid=app_a9d2c0a01c46443f_g_715
{
"user_id": "app_a9d2c0a01c46443f_g_715",
"display_name": "app_a9d2c0a01c46443f_g_715",
"email": "",
"suspended": 0,
"max_buckets": 1,
"auid": 0,
"subusers": [],
"keys": [
{
"user": "app_a9d2c0a01c46443f_g_715",
"access_key": "Z5WE64O1FSIX385T2FFU",
"secret_key": "4Vkl4aQ9Rwj5zDwRTIQTVYb2SfENVssayTezbSpv"
}
],
"swift_keys": [],
"caps": [
{
"type": "buckets",
"perm": "read"
},
{
"type": "metadata",
"perm": "*"
}
],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": true,
"max_size_kb": 4294967296,
"max_objects": -1
},
"temp_url_keys": []
}
# radosgw-admin quota set --max-objects=-1 --max-size=-1 --quota-scope=user --uid=app_a9d2c0a01c46443f_g_715
# radosgw-admin quota set --max-objects=-1 --max-size=-1 --quota-scope=bucket --bucket=app_a9d2c0a01c46443f_g_715_bucket
# radosgw-admin quota disable --quota-scope=user --uid=app_a9d2c0a01c46443f_g_71
# radosgw-admin quota enable --quota-scope=user --uid=app_a9d2c0a01c46443f_g_715
# radosgw-admin user stats --uid=app_a9d2c0a01c46443f_g_71 --sync-stats //同步
5. 查看某一个池的副本数
# ceph osd pool get .rgw.buckets size
size: 3
6. 安装boto
# pip install boto
7. python调用s3接口
7.1 测试调用s3cmd
import boto
import boto.s3.connection
access_key = 'A5DXX3XCFX7OF4ZM1UY5'
secret_key = 'r1eeS2Sq5fuQF8JbOxjlauNDkPV7apZ6sArhFlUq'
conn = boto.connect_s3(
aws_access_key_id = access_key,
aws_secret_access_key = secret_key,
host = '10.17.155.100',
port = 7480,
is_secure=False, # uncomment if you are not using ssl
calling_format = boto.s3.connection.OrdinaryCallingFormat(),
)
# list bucket
print 'List all buckets:'
buckets = conn.get_all_buckets()
for bucket in buckets:
print bucket.name
运行(主要这里只能列出owner为上述ak/sk的bucket):
List all buckets:
chaoge_100_test
chaoge_10_test
chaoge_11_test
chaoge_25_test
chaoge_26_test
chaoge_27_test
chaoge_28_test
chaoge_29_test
chaoge_2_test
chaoge_55_test
chaoge_56_test
chaoge_57_test
chaoge_58_test
chaoge_59_test
chaoge_5_test
chaoge_60_test
chaoge_61_test
chaoge_62_test
chaoge_63_test
chaoge_64_test
chaoge_65_test
chaoge_66_test
chaoge_67_test
chaoge_68_test
7.2 鉴权
s3_client.py
文件:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import sys
import httplib
import hashlib
import time
import json
import datetime
import hmac
import base64
s3ParamsToSign = {
"acl": True,
"delete": True,
"location": True,
"logging": True,
"notification": True,
"partNumber": True,
"policy": True,
"requestPayment": True,
"torrent": True,
"uploadId": True,
"uploads": True,
"versionId": True,
"versioning": True,
"versions": True,
"response-content-type": True,
"response-content-language": True,
"response-expires": True,
"response-cache-control": True,
"response-content-disposition": True,
"response-content-encoding": True
}
# Note: this function just for oss_upload
def sendrequest_v2(method, restful_server, urlpath, http_headers, parameters, body):
# process the params
if parameters is not None:
i = 0
for k in parameters.keys():
if i == 0:
urlpath += '?'
else:
urlpath += '&'
i = i+1
urlpath += str(k) + '=' + str(parameters[k])
convert_httpheaders = {}
for k in http_headers.iterkeys():
v = http_headers.get(k)
convert_httpheaders[k] = v[0]
print 'urlpath=%s' % urlpath
print convert_httpheaders
print 'body=%s' % body
conn = httplib.HTTPConnection(restful_server)
conn.request(method, urlpath, body, convert_httpheaders)
response = conn.getresponse()
resp_body = response.read()
resp_status = response.status
resp_headers = response.getheaders()
conn.close()
#return resp_status, resp_headers, resp_body
if resp_status == 200:
dict_rsp = {}
etag = response.getheader("etag", None)
if etag == None:
dict_rsp['code'] = -1
dict_rsp['msg'] = 'no etag'
else:
dict_rsp['code'] = 0
dict_rsp['msg'] = 'successful'
dict_rsp['etag'] = eval(etag)
return dict_rsp
else:
dict_error = {}
dict_error['code'] = -1
dict_error['httpcode'] = resp_status
dict_error['msg'] = "system error"
return dict_error
# Note: this function just for oss_upload
def sendrequest_v3(method, restful_server, urlpath, http_headers, parameters, body):
# process the params
if parameters is not None:
i = 0
for k in parameters.keys():
if i == 0:
urlpath += '?'
else:
urlpath += '&'
i = i + 1
urlpath += str(k) + '=' + str(parameters[k])
convert_httpheaders = {}
for k in http_headers.iterkeys():
v = http_headers.get(k)
convert_httpheaders[k] = v[0]
print 'urlpath=%s' % urlpath
print convert_httpheaders
print 'body=%s' % body
conn = httplib.HTTPConnection(restful_server)
conn.request(method, urlpath, body, convert_httpheaders)
response = conn.getresponse()
resp_body = response.read()
resp_status = response.status
resp_headers = response.getheaders()
conn.close()
if resp_status == 200:
dict_rsp = {}
dict_rsp['code'] = 0
dict_rsp['data'] = resp_body
return dict_rsp
else:
dict_error={}
dict_error['code'] = -1
dict_error['httpcode'] = resp_status
dict_error['msg'] = "system error"
dict_error['data'] = resp_body
return dict_error
# Note: this function just for oss_upload
def sendrequest_v4(method, restful_server, urlpath, http_headers, parameters, body):
# process the params
if parameters is not None:
i = 0
for k in parameters.keys():
if i == 0:
urlpath += '?'
else:
urlpath += '&'
i = i + 1
urlpath += str(k) + '=' + str(parameters[k])
convert_httpheaders = {}
for k in http_headers.iterkeys():
v = http_headers.get(k)
convert_httpheaders[k] = v[0]
print 'urlpath=%s' % urlpath
print convert_httpheaders
print 'body=%s' % body
conn = httplib.HTTPConnection(restful_server)
conn.request(method, urlpath, body, convert_httpheaders)
response = conn.getresponse()
resp_body = response.read()
resp_status = response.status
resp_headers = response.getheaders()
conn.close()
if resp_status == 200:
dict_rsp = {}
dict_rsp['code'] = 0
dict_rsp['file-length'] = resp_headers
return dict_rsp
else:
dict_error={}
dict_error['code'] = -1
dict_error['httpcode'] = resp_status
dict_error['msg'] = "system error"
dict_error['data'] = resp_body
return dict_error
class s3client:
def __init__(self, restful_server, access_key, secret_key):
self.restful_server = restful_server
self.access_key = access_key
self.secret_key = secret_key
# Note1: here http_headers is a directory like: http_headers["header-value"] = [value1, value2]
# Note2: the canonical_path may need urlencoded(here we don't process it)
# Note3: the canonical_path should be canonical(how to canonical? call AmazonEscape())
def sign(self, method, canonical_path, parameters, http_headers):
if self.secret_key == "":
return # no auth secret; skip signing, e.g. for public read-only buckets
md5 = ""
ctype = ""
xamzdate = False
date = ""
xamz_elems = []
for k in http_headers.iterkeys():
v = http_headers.get(k)
k = k.lower()
if k == "content-md5":
md5 = v[0]
elif k == "content-type":
ctype = v[0]
elif k == "date":
if xamzdate == False:
date = v[0]
else:
if k.startswith("x-amz-",0,len(k)):
str = k + ":"
for i, val in enumerate(v):
str += val
if i != len(v) -1:
str += ","
xamz_elems.append(str)
if k == "x-amz-date":
xamzdate = True
date = ""
xamz = ""
if len(xamz_elems) > 0:
xamz_elems.sort()
# print xamz_elems
for elem in xamz_elems:
xamz += (elem + "\n")
# print xamz
expires = False
if parameters.has_key("Expires"):
# Query string request authentication alternative
expires = True
date = parameters["Expires"]
parameters["AWSAccessKeyId"] = self.access_key
# process the parameters
signParams = []
for k, v in parameters.iteritems():
if s3ParamsToSign.has_key(k) and s3ParamsToSign[k] == True:
if v == "":
signParams.append(k)
else:
signParams.append(k + "=" + v)
#print signParams
if len(signParams):
signParams.sort()
canonical_path = canonical_path + "?"
for i, v in enumerate(signParams):
canonical_path += v
if i != len(signParams) -1:
canonical_path += "&"
print canonical_path
payload = method + "\n" + md5 + "\n" + ctype + "\n" + date + "\n" + xamz + canonical_path
print "payload: %s" %payload
#byte_sk = bytearray(self.secret_key, "utf-8")
#hmac_sha = hmac.new(byte_sk, digestmod=hashlib.sha1)
hmac_sha = hmac.new(self.secret_key, digestmod=hashlib.sha1)
hmac_sha.update(payload)
signature = base64.b64encode(hmac_sha.digest())
print "signature: %s" % signature
if expires:
parameters["Signature"] = signature
else:
str = "AWS " + self.access_key + ":" + signature
http_headers["Authorization"] = [str] #["AWS " + self.access_key + ":" + signature]
def multiUploadPart(self, bucket_name, object_name, part_number, upload_id, byte_data):
canonical_path = "/" + bucket_name + "/" + object_name
parameters = {}
parameters["uploadId"] = upload_id
parameters["partNumber"] = part_number
http_headers = {}
http_headers["Content-Type"] = ["binary/octet-stream"]
http_headers["Content-Length"] = [len(byte_data)]
hash_md5 = hashlib.md5(byte_data)
digest = hash_md5.digest()
md5b64 = base64.standard_b64encode(digest)
http_headers["Content-MD5"] = [md5b64]
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
http_headers["x-amz-date"] = [datetime.datetime.utcnow().strftime(GMT_FORMAT)]
self.sign("PUT", canonical_path, parameters,http_headers)
print "canonical_path: %s" % canonical_path
print "parameters: %s" % parameters
print "http_headers", http_headers
resp = sendrequest_v2("PUT", self.restful_server, canonical_path, http_headers,parameters, byte_data)
print resp
return resp
def simpleUpload(self, bucket_name, object_name, acl, byte_data):
canonical_path = "/" + bucket_name + "/" + object_name
parameters = {}
http_headers = {}
http_headers["Content-Type"] = ["binary/octet-stream"]
http_headers["Content-Length"] = [len(byte_data)]
hash_md5 = hashlib.md5(byte_data)
digest = hash_md5.digest()
md5b64 = base64.standard_b64encode(digest)
http_headers["Content-MD5"] = [md5b64]
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
http_headers["x-amz-date"] = [datetime.datetime.utcnow().strftime(GMT_FORMAT)]
#"public-read" # here default private # if bucket is private, the object should be private
#http_headers["x-amz-acl"] = ["public-read"]
http_headers["x-amz-acl"] = [acl]
self.sign("PUT", canonical_path, parameters, http_headers)
print "canonical_path: %s" % canonical_path
print "parameters: %s" % parameters
print "http_headers", http_headers
resp = sendrequest_v3("PUT", self.restful_server, canonical_path, http_headers, {}, byte_data)
print resp
return resp
def simpleDownload(self, bucket_name, object_name):
canonical_path = "/" + bucket_name + "/" + object_name
http_headers = {}
http_headers["Content-Length"] = [0]
http_headers["Accept-Encoding"] = ["gzip"]
http_headers["Range"] = ["bytes=20-24"]
#http_headers["range"] = ["Range:bytes=0-20"]
#http_headers["range"] = ["Range:bytes=56099557-56099800"]
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
http_headers["x-amz-date"] = [datetime.datetime.utcnow().strftime(GMT_FORMAT)]
self.sign("GET", canonical_path,{},http_headers)
print "canonical_path: %s" % canonical_path
print "http_headers: %s", http_headers
resp = sendrequest_v3("GET", self.restful_server, canonical_path, http_headers, {}, None)
print resp
return resp
# expiry must smaller than 30min
def generateDownloadURL(self, bucket_name, object_name, expiry):
canonical_path = "/" + bucket_name + "/" + object_name
http_headers = {}
print "now:" , time.time()
# expires = datetime.datetime.utcnow() + expiry
expires = int(time.time()) + expiry
http_parameters = {"Expires" : str(expires)}
self.sign("GET", canonical_path, http_parameters, http_headers)
signedURL = self.restful_server + canonical_path
i = 0
for k, v in http_parameters.iteritems():
if i == 0:
signedURL += "?" + k + "=" + v
else:
signedURL += "&" + k + "=" + v
i = i+1
print "generated downloadURL: %s" % signedURL
def getContentLength(self, bucket_name, object_name):
canonical_path = "/" + bucket_name + "/" + object_name
http_headers = {}
http_headers["Content-Length"] = [0]
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
http_headers["x-amz-date"] = [datetime.datetime.utcnow().strftime(GMT_FORMAT)]
self.sign("HEAD", canonical_path, {}, http_headers)
print "canonical_path: %s" % canonical_path
print "http_headers: %s", http_headers
resp = sendrequest_v4("HEAD", self.restful_server, canonical_path, http_headers, {}, None)
print resp
return resp
def removeObject(self, bucket_name, object_name):
canonical_path = "/" + bucket_name + "/" + object_name
http_headers = {}
#http_headers["Content-Length"] = [0]
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
http_headers["x-amz-date"] = [datetime.datetime.utcnow().strftime(GMT_FORMAT)]
self.sign("DELETE", canonical_path, {}, http_headers)
print "canonical_path: %s" % canonical_path
print "http_headers: %s", http_headers
resp = sendrequest_v3("DELETE", self.restful_server, canonical_path, http_headers, {}, None)
print resp
return resp
测试s3_test.py
:
# -*- coding:utf-8 -*-
import s3_client
import hashlib
import datetime
import base64
import urllib
def testSimpleUpload():
UPLOAD_SERVER = "192.168.1.211:7480"
access_key = "6B6QT7VRNKGIGB712ED8"
secret_key = "NVWLz0Q9DPvBZZIBFq7ORLGRLwg3k1hmZoTXMeIo"
client = s3_client.s3client(UPLOAD_SERVER, access_key, secret_key)
str = "hello,world"
data = bytearray(str, "utf-8")
object_name = "helloworld_7"
bucket_name = "ivan_test"
client.simpleUpload(bucket_name, object_name, "private", data)
def testGenerateDownloadURL():
DOWNLOAD_SERVER = "192.168.1.211:7480"
access_key = "6B6QT7VRNKGIGB712ED8"
secret_key = "NVWLz0Q9DPvBZZIBFq7ORLGRLwg3k1hmZoTXMeIo"
bucket_name = "ivan_test"
object_name = "helloworld_7"
expiry = 1800
client = s3_client.s3client(DOWNLOAD_SERVER, access_key, secret_key)
client.generateDownloadURL(bucket_name, object_name, expiry)
if __name__ == "__main__":
handler = "generateDownloadURL"
if handler == "generateDownloadURL":
testGenerateDownloadURL()
else:
print "unsupported handler '%s'" % handler
关于如何对path进行canonical,如下我们给出部分示例(go 版本):
// amazonShouldEscape returns true if byte should be escaped
func amazonShouldEscape(c byte) bool {
return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == '~' || c == '.' || c == '/' || c == ':')
}
func AmazonEscape(s string) string {
hexCount := 0
for i := 0; i < len(s); i++ {
if amazonShouldEscape(s[i]) {
hexCount++
}
}
if hexCount == 0 {
return s
}
t := make([]byte, len(s)+2*hexCount)
j := 0
for i := 0; i < len(s); i++ {
if c := s[i]; amazonShouldEscape(c) {
t[j] = '%'
t[j+1] = "0123456789ABCDEF"[c>>4]
t[j+2] = "0123456789ABCDEF"[c&15]
j += 3
} else {
t[j] = s[i]
j++
}
}
return string(t)
}
[参看]
boto3 github
s3 python
ceph对象存储(rgw)服务、高可用安装配置
s3接口认证说明
S3 用户手册
Amazon S3
Amazon SDK
CEPH 对象存储的系统池介绍