From 620626f27a60e0f4992e4853b48b9286ab1ccdbd Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Fri, 18 Jun 2021 11:18:12 +0800 Subject: [PATCH 1/4] add transfer acceleration --- oss2/exceptions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/oss2/exceptions.py b/oss2/exceptions.py index cff7d1e6..5638a15b 100644 --- a/oss2/exceptions.py +++ b/oss2/exceptions.py @@ -309,6 +309,9 @@ class InvalidWORMConfiguration(ServerError): status = 400 code = 'InvalidWORMConfiguration' +class NoSuchTransferAccelerationConfiguration(ServerError): + status = 404 + code = 'NoSuchTransferAccelerationConfiguration' def make_exception(resp): status = resp.status From 8744f9958535d19c1e140dee73a0eb6fd70f54ab Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Fri, 18 Jun 2021 11:33:38 +0800 Subject: [PATCH 2/4] add transfer acceleration --- oss2/api.py | 28 +++++++++++++ oss2/auth.py | 2 +- oss2/models.py | 11 +++++ oss2/xml_utils.py | 10 +++++ tests/test_bucket_transfer_acceleration.py | 17 ++++++++ unittests/test_bucket.py | 49 ++++++++++++++++++++++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tests/test_bucket_transfer_acceleration.py diff --git a/oss2/api.py b/oss2/api.py index a9994713..a11396ec 100644 --- a/oss2/api.py +++ b/oss2/api.py @@ -397,6 +397,7 @@ class Bucket(_Base): REPLICATION = "replication" REPLICATION_LOCATION = 'replicationLocation' REPLICATION_PROGRESS = 'replicationProgress' + TRANSFER_ACCELERATION = 'transferAcceleration' def __init__(self, auth, endpoint, bucket_name, @@ -2492,6 +2493,33 @@ def _get_bucket_config(self, config): logger.debug("Get bucket config done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) return resp + def put_bucket_transfer_acceleration(self, enabled): + """为存储空间(Bucket)配置传输加速 + + :param str enabled : 是否开启传输加速。true:开启传输加速; false:关闭传输加速. + :return: :class:`RequestResult ` + """ + logger.debug("Start to bucket transfer acceleration, bucket: {0}, enabled: {1}." + .format(self.bucket_name, enabled)) + data = xml_utils.to_put_bucket_transfer_acceleration(enabled) + headers = http.CaseInsensitiveDict() + headers['Content-MD5'] = utils.content_md5(data) + resp = self.__do_bucket('PUT', data=data, params={Bucket.TRANSFER_ACCELERATION: ''}, headers=headers) + logger.debug("bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) + + return RequestResult(resp) + + def get_bucket_transfer_acceleration(self): + """获取目标存储空间(Bucket)的传输加速配置 + + :return: :class:`GetBucketReplicationResult ` + """ + logger.debug("Start to get bucket transfer acceleration: {0}".format(self.bucket_name)) + resp = self.__do_bucket('GET', params={Bucket.TRANSFER_ACCELERATION: ''}) + logger.debug("Get bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) + + return self._parse_result(resp, xml_utils.parse_get_bucket_transfer_acceleration_result, GetBucketTransferAccelerationResult) + def __do_object(self, method, key, **kwargs): return self._do(method, self.bucket_name, key, **kwargs) diff --git a/oss2/auth.py b/oss2/auth.py index f756ef20..4e1a6366 100644 --- a/oss2/auth.py +++ b/oss2/auth.py @@ -83,7 +83,7 @@ class ProviderAuth(AuthBase): 'versioning', 'versionId', 'policy', 'requestPayment', 'x-oss-traffic-limit', 'qosInfo', 'asyncFetch', 'x-oss-request-payer', 'sequential', 'inventory', 'inventoryId', 'continuation-token', 'callback', 'callback-var', 'worm', 'wormId', 'wormExtend', 'replication', 'replicationLocation', - 'replicationProgress'] + 'replicationProgress', 'transferAcceleration'] ) def _sign_request(self, req, bucket_name, key): diff --git a/oss2/models.py b/oss2/models.py index 6024f1ea..a26226d2 100644 --- a/oss2/models.py +++ b/oss2/models.py @@ -2175,3 +2175,14 @@ class GetBucketReplicationProgressResult(RequestResult): def __init__(self, resp): super(GetBucketReplicationProgressResult, self).__init__(resp) self.progress = None + + +class GetBucketTransferAccelerationResult(RequestResult): + """获取目标存储空间(Bucket)的传输加速配置。 + + :param enabled: Bucket传输加速状态 + :type progress: class:`GetBucketTransferAccelerationResult `。 + """ + def __init__(self, resp): + super(GetBucketTransferAccelerationResult, self).__init__(resp) + self.enabled = None diff --git a/oss2/xml_utils.py b/oss2/xml_utils.py index dcb45ca2..0223e3db 100644 --- a/oss2/xml_utils.py +++ b/oss2/xml_utils.py @@ -1694,3 +1694,13 @@ def parse_get_bucket_replication_progress_result(result, body): result.progress = progress + +def to_put_bucket_transfer_acceleration(enabled): + root = ElementTree.Element('TransferAccelerationConfiguration') + _add_text_child(root, 'Enabled', str(enabled)) + return _node_to_string(root) + + +def parse_get_bucket_transfer_acceleration_result(result, body): + root = ElementTree.fromstring(body) + result.enabled = _find_tag(root, "Enabled") diff --git a/tests/test_bucket_transfer_acceleration.py b/tests/test_bucket_transfer_acceleration.py new file mode 100644 index 00000000..b47b8fd0 --- /dev/null +++ b/tests/test_bucket_transfer_acceleration.py @@ -0,0 +1,17 @@ +from .common import * + + +class TestBucketTransferAccelerat(OssTestCase): + def test_bucket_transfer_acceleration_normal(self): + result = self.bucket.put_bucket_transfer_acceleration('true') + self.assertEqual(200, result.status) + + get_result = self.bucket.get_bucket_transfer_acceleration() + self.assertEqual('true', get_result.enabled) + + def test_bucket_worm_illegal(self): + self.assertRaises(oss2.exceptions.NoSuchTransferAccelerationConfiguration, self.bucket.get_bucket_transfer_acceleration) + + +if __name__ == '__main__': + unittest.main() diff --git a/unittests/test_bucket.py b/unittests/test_bucket.py index b3ff6c3e..9ad15cfb 100644 --- a/unittests/test_bucket.py +++ b/unittests/test_bucket.py @@ -820,6 +820,55 @@ def test_delete_bucket_policy(self, do_request): self.assertRequest(req_info, request_text) + @patch('oss2.Session.do_request') + def test_put_bucket_transfer_acceleration(self, do_request): + request_text = '''PUT /?transferAcceleration HTTP/1.1 +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length:443 +Host: ming-oss-share.oss-cn-hangzhou.aliyuncs.com +Authorization: OSS qn6qrrqxo2oawuk53otf****:PYbzsdWAIWAlMW8luk**** + + +true +''' + + response_text = '''HTTP/1.1 200 OK +x-oss-request-id: 534B371674A4D890**** +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length: 443 +Connection: keep-alive +Server: AliyunOSS''' + + req_info = mock_response(do_request, response_text) + + result = bucket().put_bucket_transfer_acceleration('true') + + self.assertRequest(req_info, request_text) + + @patch('oss2.Session.do_request') + def test_get_bucket_transfer_acceleration(self, do_request): + request_text = '''GET /?transferAcceleration HTTP/1.1 +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length:443 +Host: ming-oss-share.oss-cn-hangzhou.aliyuncs.com +Authorization: OSS qn6qrrqxo2oawuk53otf****:PYbzsdWAIWAlMW8luk****''' + + response_text = '''HTTP/1.1 200 OK +x-oss-request-id: 534B371674E88A4D8906**** +Date: Fri , 30 Apr 2021 13:08:38 GMT + + + + true +''' + + req_info = mock_response(do_request, response_text) + + result = bucket().get_bucket_transfer_acceleration() + + self.assertRequest(req_info, request_text) + self.assertEqual(result.enabled, 'true') + if __name__ == '__main__': unittest.main() From 5705b07ff55108e5ef0357c76e76a0f4819ddbb9 Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 <85101990+zhuxiaolong37@users.noreply.github.com> Date: Tue, 29 Jun 2021 11:34:37 +0800 Subject: [PATCH 3/4] Dev 2106 (#282) * add transfer acceleration --- oss2/api.py | 28 +++++++++++++ oss2/auth.py | 2 +- oss2/exceptions.py | 3 ++ oss2/models.py | 11 +++++ oss2/xml_utils.py | 10 +++++ tests/test_bucket_transfer_acceleration.py | 17 ++++++++ unittests/test_bucket.py | 49 ++++++++++++++++++++++ 7 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/test_bucket_transfer_acceleration.py diff --git a/oss2/api.py b/oss2/api.py index a9994713..a11396ec 100644 --- a/oss2/api.py +++ b/oss2/api.py @@ -397,6 +397,7 @@ class Bucket(_Base): REPLICATION = "replication" REPLICATION_LOCATION = 'replicationLocation' REPLICATION_PROGRESS = 'replicationProgress' + TRANSFER_ACCELERATION = 'transferAcceleration' def __init__(self, auth, endpoint, bucket_name, @@ -2492,6 +2493,33 @@ def _get_bucket_config(self, config): logger.debug("Get bucket config done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) return resp + def put_bucket_transfer_acceleration(self, enabled): + """为存储空间(Bucket)配置传输加速 + + :param str enabled : 是否开启传输加速。true:开启传输加速; false:关闭传输加速. + :return: :class:`RequestResult ` + """ + logger.debug("Start to bucket transfer acceleration, bucket: {0}, enabled: {1}." + .format(self.bucket_name, enabled)) + data = xml_utils.to_put_bucket_transfer_acceleration(enabled) + headers = http.CaseInsensitiveDict() + headers['Content-MD5'] = utils.content_md5(data) + resp = self.__do_bucket('PUT', data=data, params={Bucket.TRANSFER_ACCELERATION: ''}, headers=headers) + logger.debug("bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) + + return RequestResult(resp) + + def get_bucket_transfer_acceleration(self): + """获取目标存储空间(Bucket)的传输加速配置 + + :return: :class:`GetBucketReplicationResult ` + """ + logger.debug("Start to get bucket transfer acceleration: {0}".format(self.bucket_name)) + resp = self.__do_bucket('GET', params={Bucket.TRANSFER_ACCELERATION: ''}) + logger.debug("Get bucket transfer acceleration done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) + + return self._parse_result(resp, xml_utils.parse_get_bucket_transfer_acceleration_result, GetBucketTransferAccelerationResult) + def __do_object(self, method, key, **kwargs): return self._do(method, self.bucket_name, key, **kwargs) diff --git a/oss2/auth.py b/oss2/auth.py index f756ef20..4e1a6366 100644 --- a/oss2/auth.py +++ b/oss2/auth.py @@ -83,7 +83,7 @@ class ProviderAuth(AuthBase): 'versioning', 'versionId', 'policy', 'requestPayment', 'x-oss-traffic-limit', 'qosInfo', 'asyncFetch', 'x-oss-request-payer', 'sequential', 'inventory', 'inventoryId', 'continuation-token', 'callback', 'callback-var', 'worm', 'wormId', 'wormExtend', 'replication', 'replicationLocation', - 'replicationProgress'] + 'replicationProgress', 'transferAcceleration'] ) def _sign_request(self, req, bucket_name, key): diff --git a/oss2/exceptions.py b/oss2/exceptions.py index cff7d1e6..5638a15b 100644 --- a/oss2/exceptions.py +++ b/oss2/exceptions.py @@ -309,6 +309,9 @@ class InvalidWORMConfiguration(ServerError): status = 400 code = 'InvalidWORMConfiguration' +class NoSuchTransferAccelerationConfiguration(ServerError): + status = 404 + code = 'NoSuchTransferAccelerationConfiguration' def make_exception(resp): status = resp.status diff --git a/oss2/models.py b/oss2/models.py index 6024f1ea..a26226d2 100644 --- a/oss2/models.py +++ b/oss2/models.py @@ -2175,3 +2175,14 @@ class GetBucketReplicationProgressResult(RequestResult): def __init__(self, resp): super(GetBucketReplicationProgressResult, self).__init__(resp) self.progress = None + + +class GetBucketTransferAccelerationResult(RequestResult): + """获取目标存储空间(Bucket)的传输加速配置。 + + :param enabled: Bucket传输加速状态 + :type progress: class:`GetBucketTransferAccelerationResult `。 + """ + def __init__(self, resp): + super(GetBucketTransferAccelerationResult, self).__init__(resp) + self.enabled = None diff --git a/oss2/xml_utils.py b/oss2/xml_utils.py index dcb45ca2..0223e3db 100644 --- a/oss2/xml_utils.py +++ b/oss2/xml_utils.py @@ -1694,3 +1694,13 @@ def parse_get_bucket_replication_progress_result(result, body): result.progress = progress + +def to_put_bucket_transfer_acceleration(enabled): + root = ElementTree.Element('TransferAccelerationConfiguration') + _add_text_child(root, 'Enabled', str(enabled)) + return _node_to_string(root) + + +def parse_get_bucket_transfer_acceleration_result(result, body): + root = ElementTree.fromstring(body) + result.enabled = _find_tag(root, "Enabled") diff --git a/tests/test_bucket_transfer_acceleration.py b/tests/test_bucket_transfer_acceleration.py new file mode 100644 index 00000000..b47b8fd0 --- /dev/null +++ b/tests/test_bucket_transfer_acceleration.py @@ -0,0 +1,17 @@ +from .common import * + + +class TestBucketTransferAccelerat(OssTestCase): + def test_bucket_transfer_acceleration_normal(self): + result = self.bucket.put_bucket_transfer_acceleration('true') + self.assertEqual(200, result.status) + + get_result = self.bucket.get_bucket_transfer_acceleration() + self.assertEqual('true', get_result.enabled) + + def test_bucket_worm_illegal(self): + self.assertRaises(oss2.exceptions.NoSuchTransferAccelerationConfiguration, self.bucket.get_bucket_transfer_acceleration) + + +if __name__ == '__main__': + unittest.main() diff --git a/unittests/test_bucket.py b/unittests/test_bucket.py index b3ff6c3e..9ad15cfb 100644 --- a/unittests/test_bucket.py +++ b/unittests/test_bucket.py @@ -820,6 +820,55 @@ def test_delete_bucket_policy(self, do_request): self.assertRequest(req_info, request_text) + @patch('oss2.Session.do_request') + def test_put_bucket_transfer_acceleration(self, do_request): + request_text = '''PUT /?transferAcceleration HTTP/1.1 +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length:443 +Host: ming-oss-share.oss-cn-hangzhou.aliyuncs.com +Authorization: OSS qn6qrrqxo2oawuk53otf****:PYbzsdWAIWAlMW8luk**** + + +true +''' + + response_text = '''HTTP/1.1 200 OK +x-oss-request-id: 534B371674A4D890**** +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length: 443 +Connection: keep-alive +Server: AliyunOSS''' + + req_info = mock_response(do_request, response_text) + + result = bucket().put_bucket_transfer_acceleration('true') + + self.assertRequest(req_info, request_text) + + @patch('oss2.Session.do_request') + def test_get_bucket_transfer_acceleration(self, do_request): + request_text = '''GET /?transferAcceleration HTTP/1.1 +Date: Fri , 30 Apr 2021 13:08:38 GMT +Content-Length:443 +Host: ming-oss-share.oss-cn-hangzhou.aliyuncs.com +Authorization: OSS qn6qrrqxo2oawuk53otf****:PYbzsdWAIWAlMW8luk****''' + + response_text = '''HTTP/1.1 200 OK +x-oss-request-id: 534B371674E88A4D8906**** +Date: Fri , 30 Apr 2021 13:08:38 GMT + + + + true +''' + + req_info = mock_response(do_request, response_text) + + result = bucket().get_bucket_transfer_acceleration() + + self.assertRequest(req_info, request_text) + self.assertEqual(result.enabled, 'true') + if __name__ == '__main__': unittest.main() From 9d9cc761d3cb325a9c4bc1d622a3580fbee1ac9a Mon Sep 17 00:00:00 2001 From: zhuxiaolong37 Date: Thu, 8 Jul 2021 11:45:59 +0800 Subject: [PATCH 4/4] add lifecycle overlap --- oss2/api.py | 5 ++-- oss2/headers.py | 2 ++ tests/test_bucket.py | 40 +++++++++++++++++++++++++++++++- unittests/test_bucket.py | 50 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/oss2/api.py b/oss2/api.py index a11396ec..eb6a38fa 100644 --- a/oss2/api.py +++ b/oss2/api.py @@ -1625,14 +1625,15 @@ def delete_bucket_cors(self): logger.debug("Delete bucket CORS done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) return RequestResult(resp) - def put_bucket_lifecycle(self, input): + def put_bucket_lifecycle(self, input, headers=None): """设置生命周期管理的配置。 :param input: :class:`BucketLifecycle ` 对象或其他 """ + headers = http.CaseInsensitiveDict(headers) data = self.__convert_data(BucketLifecycle, xml_utils.to_put_bucket_lifecycle, input) logger.debug("Start to put bucket lifecycle, bucket: {0}, lifecycle: {1}".format(self.bucket_name, data)) - resp = self.__do_bucket('PUT', data=data, params={Bucket.LIFECYCLE: ''}) + resp = self.__do_bucket('PUT', data=data, params={Bucket.LIFECYCLE: ''}, headers=headers) logger.debug("Put bucket lifecycle done, req_id: {0}, status_code: {1}".format(resp.request_id, resp.status)) return RequestResult(resp) diff --git a/oss2/headers.py b/oss2/headers.py index d5e7ef46..956a9fbf 100644 --- a/oss2/headers.py +++ b/oss2/headers.py @@ -67,6 +67,8 @@ OSS_METADATA_DIRECTIVE = 'x-oss-metadata-directive' +OSS_ALLOW_ACTION_OVERLAP = "x-oss-allow-same-action-overlap" + class RequestHeader(dict): def __init__(self, *arg, **kw): super(RequestHeader, self).__init__(*arg, **kw) diff --git a/tests/test_bucket.py b/tests/test_bucket.py index ca8298f3..f27efbc9 100644 --- a/tests/test_bucket.py +++ b/tests/test_bucket.py @@ -6,7 +6,7 @@ from .common import * from oss2 import to_string - +from oss2.headers import * class TestBucket(OssTestCase): def test_bucket(self): @@ -171,6 +171,44 @@ def test_lifecycle_days(self): self.assertRaises(oss2.exceptions.NoSuchLifecycle, self.bucket.get_bucket_lifecycle) + def test_lifecycle_overlap(self): + from oss2.models import LifecycleExpiration, LifecycleRule, BucketLifecycle + + rule = LifecycleRule(random_string(10), '中文前缀/', + status=LifecycleRule.ENABLED, + expiration=LifecycleExpiration(date=datetime.date(2016, 12, 25))) + lifecycle = BucketLifecycle([rule]) + + rule2 = LifecycleRule(random_string(10), '中文前缀/2', + status=LifecycleRule.ENABLED, + expiration=LifecycleExpiration(date=datetime.date(2016, 12, 25))) + lifecycle.rules.append(rule2) + + headers = dict() + headers[OSS_ALLOW_ACTION_OVERLAP] = 'true' + self.bucket.put_bucket_lifecycle(lifecycle, headers) + self.retry_assert(lambda: self.same_lifecycle(rule, self.bucket)) + + self.bucket.delete_bucket_lifecycle() + + def test_lifecycle_overlap_exception(self): + from oss2.models import LifecycleExpiration, LifecycleRule, BucketLifecycle + + rule = LifecycleRule(random_string(10), '前缀/', + status=LifecycleRule.ENABLED, + expiration=LifecycleExpiration(date=datetime.date(2016, 12, 25))) + lifecycle = BucketLifecycle([rule]) + + rule2 = LifecycleRule(random_string(10), '前缀/2', + status=LifecycleRule.ENABLED, + expiration=LifecycleExpiration(date=datetime.date(2016, 12, 25))) + lifecycle.rules.append(rule2) + try: + self.bucket.put_bucket_lifecycle(lifecycle) + except oss2.exceptions.OssError as e: + self.assertEqual(e.message, 'Overlap for same action type Expiration') + self.bucket.delete_bucket_lifecycle() + def test_put_lifecycle_days_less_than_transition_days(self): from oss2.models import LifecycleExpiration, LifecycleRule, BucketLifecycle diff --git a/unittests/test_bucket.py b/unittests/test_bucket.py index 9ad15cfb..c73b139c 100644 --- a/unittests/test_bucket.py +++ b/unittests/test_bucket.py @@ -4,7 +4,7 @@ from mock import patch from functools import partial - +from oss2.headers import * from oss2 import to_string from unittests.common import * @@ -321,6 +321,54 @@ def test_put_lifecycle_date(self, do_request): self.assertRequest(req_info, request_text.format(id, prefix, status, date)) + @patch('oss2.Session.do_request') + def test_put_lifecycle_overlap(self, do_request): + from oss2.models import LifecycleExpiration, LifecycleRule, BucketLifecycle + + request_text = '''PUT /?lifecycle= HTTP/1.1 +Host: ming-oss-share.oss-cn-hangzhou.aliyuncs.com +Accept-Encoding: identity +Connection: keep-alive +Content-Length: 198 +x-oss-allow-same-action-overlap: true +date: Sat, 12 Dec 2015 00:35:37 GMT +User-Agent: aliyun-sdk-python/2.0.2(Windows/7/;3.3.3) +Accept: */* +authorization: OSS ZCDmm7TPZKHtx77j:45HTpSD5osRvtusf8VCkmchZZFs= + +{0}{1}{2}{3}{4}{5}{6}{7}''' + + response_text = '''HTTP/1.1 200 OK +Server: AliyunOSS +Date: Sat, 12 Dec 2015 00:35:37 GMT +Content-Length: 0 +Connection: keep-alive +x-oss-request-id: 566B6BD9B295345D15740F1F''' + + id = 'hello world' + prefix = '中文前缀' + status = 'Disabled' + date = '2015-12-25T00:00:00.000Z' + id_global = 'hello global' + prefix_global = '中文-同样前缀' + status_global = 'Disabled' + date_global = '2015-12-25T00:00:00.000Z' + + headers = dict() + headers[OSS_ALLOW_ACTION_OVERLAP] = 'true' + req_info = mock_response(do_request, response_text) + rule = LifecycleRule(id, prefix, + status=LifecycleRule.DISABLED, + expiration=LifecycleExpiration(date=datetime.date(2015, 12, 25))) + rule_global = LifecycleRule(id_global, prefix_global, + status=LifecycleRule.DISABLED, + expiration=LifecycleExpiration(date=datetime.date(2015, 12, 25))) + lifecycle = BucketLifecycle([rule]) + lifecycle.rules.append(rule_global) + bucket().put_bucket_lifecycle(lifecycle, headers) + + self.assertRequest(req_info, request_text.format(id, prefix, status, date, id_global, prefix_global, status_global, date_global)) + @patch('oss2.Session.do_request') def test_put_lifecycle_days(self, do_request): from oss2.models import LifecycleExpiration, LifecycleRule, BucketLifecycle