summaryrefslogtreecommitdiffstats
path: root/examples/boto3
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /examples/boto3
parentInitial commit. (diff)
downloadceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz
ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/boto3')
-rw-r--r--examples/boto3/README.md98
-rwxr-xr-xexamples/boto3/append_object.py42
-rwxr-xr-xexamples/boto3/delete_notification.py36
-rwxr-xr-xexamples/boto3/get_notification.py28
-rwxr-xr-xexamples/boto3/get_usage_stats.py17
-rwxr-xr-xexamples/boto3/list_unordered.py25
-rwxr-xr-xexamples/boto3/notification_filters.py48
-rw-r--r--examples/boto3/service-2.sdk-extras.json215
-rw-r--r--examples/boto3/topic_attributes.py46
-rwxr-xr-xexamples/boto3/topic_with_endpoint.py41
10 files changed, 596 insertions, 0 deletions
diff --git a/examples/boto3/README.md b/examples/boto3/README.md
new file mode 100644
index 000000000..52f43746d
--- /dev/null
+++ b/examples/boto3/README.md
@@ -0,0 +1,98 @@
+# Introduction
+This directory contains examples on how to use AWS CLI/boto3 to exercise the RadosGW extensions to the S3 API.
+This is an extension to the [AWS SDK](https://github.com/boto/botocore/blob/develop/botocore/data/s3/2006-03-01/service-2.json).
+
+# Users
+For the standard client to support these extensions, the: ``service-2.sdk-extras.json`` file should be placed under: ``~/.aws/models/s3/2006-03-01/`` directory.
+For more information see [here](https://github.com/boto/botocore/blob/develop/botocore/loaders.py#L33).
+## Python
+The [boto3 client](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) could be used with the extensions, code samples exists in this directory.
+## AWS CLI
+The standard [AWS CLI](https://docs.aws.amazon.com/cli/latest/) may also be used with these extensions. For example:
+- Unordered listing:
+```
+aws --endpoint-url http://localhost:8000 s3api list-objects --bucket=mybucket --allow-unordered
+```
+
+- Topic creation with endpoint:
+```
+aws --endpoint-url http://localhost:8000 sns create-topic --name=mytopic --attributes='{"push-endpoint": "amqp://localhost:5672", "amqp-exchange": "ex1", "amqp-ack-level": "broker"}'
+```
+Expected output:
+```
+{
+ "TopicArn": "arn:aws:sns:default::mytopic"
+}
+```
+
+- Get topic attributes:
+```
+aws --endpoint-url http://localhost:8000 sns get-topic-attributes --topic-arn="arn:aws:sns:default::mytopic"
+```
+Expected output:
+```
+{
+ "Attributes": {
+ "User": "",
+ "Name": "mytopic",
+ "EndPoint": "{\"EndpointAddress\":\"amqp://localhost:5672\",\"EndpointArgs\":\"Attributes.entry.1.key=push-endpoint&Attributes.entry.1.value=amqp://localhost:5672&Attributes.entry.2.key=amqp-exchange&Attributes.entry.2.value=ex1&Attributes.entry.3.key=amqp-ack-level&Attributes.entry.3.value=broker&Version=2010-03-31&amqp-ack-level=broker&amqp-exchange=ex1&push-endpoint=amqp://localhost:5672\",\"EndpointTopic\":\"mytopic\",\"HasStoredSecret\":\"false\",\"Persistent\":\"false\"}",
+ "TopicArn": "arn:aws:sns:default::mytopic",
+ "OpaqueData": ""
+ }
+}
+```
+
+- Bucket notifications with filtering extensions (bucket must exist before calling this command):
+```
+aws --region=default --endpoint-url http://localhost:8000 s3api put-bucket-notification-configuration --bucket=mybucket --notification-configuration='{"TopicConfigurations": [{"Id": "notif1", "TopicArn": "arn:aws:sns:default::mytopic", "Events": ["s3:ObjectCreated:*", "s3:ObjectRemoved:*"], "Filter": {"Metadata": {"FilterRules": [{"Name": "x-amz-meta-foo", "Value": "bar"}, {"Name": "x-amz-meta-hello", "Value": "world"}]}, "Key": {"FilterRules": [{"Name": "regex", "Value": "([a-z]+)"}]}}}]}'
+```
+
+- Get configuration of a specific notification of a bucket:
+```
+aws --endpoint-url http://localhost:8000 s3api get-bucket-notification-configuration --bucket=mybucket --notification=notif1
+```
+Expected output:
+```
+{
+ "TopicConfigurations": [
+ {
+ "Id": "notif1",
+ "TopicArn": "arn:aws:sns:default::mytopic",
+ "Events": [
+ "s3:ObjectCreated:*",
+ "s3:ObjectRemoved:*"
+ ],
+ "Filter": {
+ "Key": {
+ "FilterRules": [
+ {
+ "Name": "regex",
+ "Value": "([a-z]+)"
+ }
+ ]
+ },
+ "Metadata": {
+ "FilterRules": [
+ {
+ "Name": "x-amz-meta-foo",
+ "Value": "bar"
+ },
+ {
+ "Name": "x-amz-meta-hello",
+ "Value": "world"
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+```
+
+# Developers
+Anyone developing an extension to the S3 API supported by AWS, please modify ``service-2.sdk-extras.json`` (all extensions should go into the same file), so that boto3 could be used to test the new API.
+In addition, python files with code samples should be added to this directory demonstrating use of the new API.
+When testing you changes please:
+- make sure that the modified file is in the boto3 path as explained above
+- make sure that the standard S3 tests suit is not broken, even with the extensions files in the path
+
diff --git a/examples/boto3/append_object.py b/examples/boto3/append_object.py
new file mode 100755
index 000000000..0e13252ec
--- /dev/null
+++ b/examples/boto3/append_object.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+from __future__ import print_function
+
+import boto3
+import sys
+import json
+
+def js_print(arg):
+ print(json.dumps(arg, indent=2))
+
+if len(sys.argv) != 3:
+ print('Usage: ' + sys.argv[0] + ' <bucket> <key>')
+ sys.exit(1)
+
+# bucket name as first argument
+bucketname = sys.argv[1]
+keyname = sys.argv[2]
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+print('deleting object first')
+js_print(client.delete_object(Bucket=bucketname, Key=keyname))
+print('appending at position 0')
+resp = client.put_object(Bucket=bucketname, Key=keyname,
+ Append=True,
+ AppendPosition=0,
+ Body='8letters')
+
+js_print(resp)
+append_pos = resp['AppendPosition']
+print('appending at position %d' % append_pos)
+js_print(client.put_object(Bucket=bucketname, Key=keyname,
+ Append=True,
+ AppendPosition=append_pos,
+ Body='8letters'))
diff --git a/examples/boto3/delete_notification.py b/examples/boto3/delete_notification.py
new file mode 100755
index 000000000..ca5958e52
--- /dev/null
+++ b/examples/boto3/delete_notification.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+import boto3
+import sys
+
+if len(sys.argv) == 3:
+ # bucket name as first argument
+ bucketname = sys.argv[1]
+ # notification name as second argument
+ notification_name = sys.argv[2]
+elif len(sys.argv) == 2:
+ # bucket name as first argument
+ bucketname = sys.argv[1]
+ notification_name = ""
+else:
+ print('Usage: ' + sys.argv[0] + ' <bucket> [notification]')
+ sys.exit(1)
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+# deleting a specific notification congifuration from a bucket (when NotificationId is provided) or
+# deleting all notification configurations on a bucket (without deleting the bucket itself) are extension to AWS S3 API
+
+if notification_name == "":
+ print(client.delete_bucket_notification_configuration(Bucket=bucketname))
+else:
+ print(client.delete_bucket_notification_configuration(Bucket=bucketname,
+ Notification=notification_name))
diff --git a/examples/boto3/get_notification.py b/examples/boto3/get_notification.py
new file mode 100755
index 000000000..490c018d4
--- /dev/null
+++ b/examples/boto3/get_notification.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import boto3
+import sys
+
+if len(sys.argv) != 3:
+ print('Usage: ' + sys.argv[0] + ' <bucket> <notification>')
+ sys.exit(1)
+
+# bucket name as first argument
+bucketname = sys.argv[1]
+ # notification name as second argument
+notification_name = sys.argv[2]
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+# getting a specific notification configuration is an extension to AWS S3 API
+
+print(client.get_bucket_notification_configuration(Bucket=bucketname,
+ Notification=notification_name))
diff --git a/examples/boto3/get_usage_stats.py b/examples/boto3/get_usage_stats.py
new file mode 100755
index 000000000..0b7880d4f
--- /dev/null
+++ b/examples/boto3/get_usage_stats.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+from __future__ import print_function
+
+import boto3
+import json
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+print(json.dumps(client.get_usage_stats(), indent=2))
diff --git a/examples/boto3/list_unordered.py b/examples/boto3/list_unordered.py
new file mode 100755
index 000000000..2aa5a8e06
--- /dev/null
+++ b/examples/boto3/list_unordered.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import boto3
+import sys
+
+if len(sys.argv) != 2:
+ print('Usage: ' + sys.argv[0] + ' <bucket>')
+ sys.exit(1)
+
+# bucket name as first argument
+bucketname = sys.argv[1]
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+# geting an unordered list of objets is an extension to AWS S3 API
+
+print(client.list_objects(Bucket=bucketname, AllowUnordered=True))
diff --git a/examples/boto3/notification_filters.py b/examples/boto3/notification_filters.py
new file mode 100755
index 000000000..2687c8b3a
--- /dev/null
+++ b/examples/boto3/notification_filters.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+import boto3
+import sys
+
+if len(sys.argv) != 4:
+ print('Usage: ' + sys.argv[0] + ' <bucket> <topic ARN> <notification Id>')
+ sys.exit(1)
+
+# bucket name as first argument
+bucketname = sys.argv[1]
+# topic ARN as second argument
+topic_arn = sys.argv[2]
+# notification id as third argument
+notification_id = sys.argv[3]
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('s3',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ aws_secret_access_key=secret_key)
+
+# regex filter on the object name and metadata based filtering are extension to AWS S3 API
+# bucket and topic should be created beforehand
+
+topic_conf_list = [{'Id': notification_id,
+ 'TopicArn': topic_arn,
+ 'Events': ['s3:ObjectCreated:*', 's3:ObjectRemoved:*'],
+ 'Filter': {
+ 'Metadata': {
+ 'FilterRules': [{'Name': 'x-amz-meta-foo', 'Value': 'bar'},
+ {'Name': 'x-amz-meta-hello', 'Value': 'world'}]
+ },
+ 'Tags': {
+ 'FilterRules': [{'Name': 'foo', 'Value': 'bar'},
+ {'Name': 'hello', 'Value': 'world'}]
+ },
+ 'Key': {
+ 'FilterRules': [{'Name': 'regex', 'Value': '([a-z]+)'}]
+ }
+ }}]
+
+print(client.put_bucket_notification_configuration(Bucket=bucketname,
+ NotificationConfiguration={'TopicConfigurations': topic_conf_list}))
diff --git a/examples/boto3/service-2.sdk-extras.json b/examples/boto3/service-2.sdk-extras.json
new file mode 100644
index 000000000..9ee66730e
--- /dev/null
+++ b/examples/boto3/service-2.sdk-extras.json
@@ -0,0 +1,215 @@
+{
+"version": 1.0,
+"merge": {
+ "operations":{
+ "DeleteBucketNotificationConfiguration":{
+ "name":"DeleteBucketNotificationConfiguration",
+ "http":{
+ "method":"DELETE",
+ "requestUri":"/{Bucket}?notification",
+ "responseCode":204
+ },
+ "input":{"shape":"DeleteBucketNotificationConfigurationRequest"},
+ "documentationUrl":"https://docs.ceph.com/docs/master/radosgw/s3/bucketops/#delete-notification",
+ "documentation":"<p>Deletes the notification configuration from the bucket.</p>"
+ },
+ "GetUsageStats":{
+ "name":"GetUsageStats",
+ "http":{
+ "method":"GET",
+ "requestUri":"/?usage",
+ "responseCode":200
+ },
+ "output": {"shape": "GetUsageStatsOutput"},
+ "documentationUrl":"https://docs.ceph.com/docs/master/radosgw/s3/serviceops#get-usage-stats",
+ "documentation":"<p>Get usage stats for the user</p>"
+ }
+ },
+ "shapes": {
+ "ListObjectsRequest": {
+ "members": {
+ "AllowUnordered": {
+ "shape":"AllowUnordered",
+ "documentation":"<p>Allow the listing results to be returned in unsorted order. This may be faster when listing very large buckets.</p>",
+ "location":"querystring",
+ "locationName":"allow-unordered"
+ }
+ }
+ },
+ "AllowUnordered":{"type":"boolean"},
+ "PutObjectRequest": {
+ "members": {
+ "AppendPosition": {
+ "shape":"AppendPosition",
+ "documentation": "<p>Position to allow appending</p>",
+ "location": "querystring",
+ "locationName": "position"
+ },
+ "Append": {
+ "shape":"Append",
+ "documentation":"<p>Append Object</p>",
+ "location": "querystring",
+ "locationName": "append"
+ }
+ }
+ },
+ "Append": {"type":"boolean"},
+ "AppendPosition":{"type":"integer"},
+ "PutObjectOutput": {
+ "members": {
+ "AppendPosition": {
+ "shape":"AppendPosition",
+ "documentation": "<p>Position to allow appending</p>",
+ "location": "header",
+ "locationName": "x-rgw-next-append-position",
+ "documentationUrl":"https://docs.ceph.com/docs/master/radosgw/s3/objectops/#append-object"
+ }
+ }
+ },
+ "GetBucketNotificationConfigurationRequest":{
+ "type":"structure",
+ "required":["Bucket"],
+ "members":{
+ "Bucket":{
+ "shape":"BucketName",
+ "documentation":"<p>Name of the bucket to get the notifications configuration for.</p>",
+ "location":"uri",
+ "locationName":"Bucket"
+ },
+ "Notification":{
+ "shape":"NotificationId",
+ "documentation":"<p>Id of the specific notification on the bucket for which the configuration should be retrieved.</p>",
+ "location":"querystring",
+ "locationName":"notification-id"
+ }
+ }
+ },
+ "DeleteBucketNotificationConfigurationRequest":{
+ "type":"structure",
+ "required":["Bucket"],
+ "members":{
+ "Bucket":{
+ "shape":"BucketName",
+ "documentation":"<p>Name of the bucket to delete the notifications configuration from.</p>",
+ "location":"uri",
+ "locationName":"Bucket"
+ },
+ "Notification":{
+ "shape":"NotificationId",
+ "documentation":"<p>Id of the specific notification on the bucket to be deleted.</p>",
+ "location":"querystring",
+ "locationName":"notification-id"
+ }
+ }
+ },
+ "FilterRule":{
+ "type":"structure",
+ "members":{
+ "Name":{
+ "shape":"FilterRuleName",
+ "documentation":"<p>The object key name prefix, suffix or regex identifying one or more objects to which the filtering rule applies. The maximum length is 1,024 characters. Overlapping prefixes and suffixes are supported.</p>"
+ },
+ "Value":{
+ "shape":"FilterRuleValue",
+ "documentation":"<p>The value that the filter searches for in object key names.</p>"
+ }
+ },
+ "documentation":"<p>Specifies the Amazon S3 object key name to filter on and whether to filter on the suffix, prefix or regex of the key name.</p>"
+ },
+ "FilterRuleName":{
+ "type":"string",
+ "enum":[
+ "prefix",
+ "suffix",
+ "regex"
+ ]
+ },
+ "NotificationConfigurationFilter":{
+ "type":"structure",
+ "members":{
+ "Key":{
+ "shape":"S3KeyFilter",
+ "documentation":"<p/>",
+ "locationName":"S3Key"
+ },
+ "Metadata":{
+ "shape":"S3MetadataFilter",
+ "documentation":"<p/>",
+ "locationName":"S3Metadata"
+ },
+ "Tags":{
+ "shape":"S3TagsFilter",
+ "documentation":"<p/>",
+ "locationName":"S3Tags"
+ }
+
+ },
+ "documentation":"<p>Specifies object key name filtering rules. For information about key name filtering, see <a href=\"https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html\">Configuring Event Notifications</a> in the <i>Amazon Simple Storage Service Developer Guide</i>.</p>"
+ },
+ "S3KeyFilter":{
+ "type":"structure",
+ "members":{
+ "FilterRules":{
+ "shape":"FilterRuleList",
+ "documentation":"<p/>",
+ "locationName":"FilterRule"
+ }
+ },
+ "documentation":"<p>A container for object key name prefix, suffix and regex filtering rules.</p>"
+ },
+ "S3MetadataFilter":{
+ "type":"structure",
+ "members":{
+ "FilterRules":{
+ "shape":"FilterRuleList",
+ "documentation":"<p/>",
+ "locationName":"FilterRule"
+ }
+ },
+ "documentation":"<p>A container for metadata filtering rules.</p>"
+ },
+ "S3TagsFilter":{
+ "type":"structure",
+ "members":{
+ "FilterRules":{
+ "shape":"FilterRuleList",
+ "documentation":"<p/>",
+ "locationName":"FilterRule"
+ }
+ },
+ "documentation":"<p>A container for object tags filtering rules.</p>"
+ },
+ "GetUsageStatsOutput": {
+ "type": "structure",
+ "members": {
+ "Summary": {
+ "shape":"UsageStatsSummary",
+ "documentation": "<p/>"
+ }
+ }
+ },
+ "UsageStatsSummary": {
+ "type": "structure",
+ "members": {
+ "QuotaMaxBytes":{"shape":"QuotaMaxBytes"},
+ "QuotaMaxBuckets":{"shape": "QuotaMaxBuckets"},
+ "QuotaMaxObjCount":{"shape":"QuotaMaxObjCount"},
+ "QuotaMaxBytesPerBucket":{"shape":"QuotaMaxBytesPerBucket"},
+ "QuotaMaxObjCountPerBucket":{"shape":"QuotaMaxObjCountPerBucket"},
+ "TotalBytes":{"shape":"TotalBytes"},
+ "TotalBytesRounded":{"shape":"TotalBytesRounded"},
+ "TotalEntries":{"shape":"TotalEntries"}
+ }
+ },
+ "QuotaMaxBytes":{"type":"integer"},
+ "QuotaMaxBuckets":{"type": "integer"},
+ "QuotaMaxObjCount":{"type":"integer"},
+ "QuotaMaxBytesPerBucket":{"type":"integer"},
+ "QuotaMaxObjCountPerBucket":{"type":"integer"},
+ "TotalBytesRounded":{"type":"integer"},
+ "TotalBytes":{"type":"integer"},
+ "TotalEntries":{"type":"integer"}
+ },
+ "documentation":"<p/>"
+}
+}
diff --git a/examples/boto3/topic_attributes.py b/examples/boto3/topic_attributes.py
new file mode 100644
index 000000000..3caeb1fec
--- /dev/null
+++ b/examples/boto3/topic_attributes.py
@@ -0,0 +1,46 @@
+import sys
+import urllib
+import hmac
+import hashlib
+import base64
+import xmltodict
+import http.client
+from urllib import parse as urlparse
+from time import gmtime, strftime
+
+if len(sys.argv) == 2:
+ # topic arn as first argument
+ topic_arn = sys.argv[1]
+else:
+ print ('Usage: ' + sys.argv[0] + ' <topic arn> [region name]')
+ sys.exit(1)
+
+# endpoint and keys from vstart
+endpoint = '127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+
+parameters = {'Action': 'GetTopic', 'TopicArn': topic_arn}
+body = urlparse.urlencode(parameters)
+string_date = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
+content_type = 'application/x-www-form-urlencoded; charset=utf-8'
+resource = '/'
+method = 'POST'
+string_to_sign = method + '\n\n' + content_type + '\n' + string_date + '\n' + resource
+signature = base64.b64encode(hmac.new(secret_key.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha1).digest()).decode('ascii')
+headers = {'Authorization': 'AWS '+access_key+':'+signature,
+ 'Date': string_date,
+ 'Host': endpoint,
+ 'Content-Type': content_type}
+http_conn = http.client.HTTPConnection(endpoint)
+http_conn.request(method, resource, body, headers)
+response = http_conn.getresponse()
+data = response.read()
+status = response.status
+http_conn.close()
+dict_response = xmltodict.parse(data)
+
+# getting attributes of a specific topic is an extension to AWS sns
+
+print(dict_response, status)
diff --git a/examples/boto3/topic_with_endpoint.py b/examples/boto3/topic_with_endpoint.py
new file mode 100755
index 000000000..3137cee7d
--- /dev/null
+++ b/examples/boto3/topic_with_endpoint.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+import boto3
+import sys
+import urlparse
+from botocore.client import Config
+
+if len(sys.argv) == 3:
+ # topic name as first argument
+ topic_name = sys.argv[1]
+ # region name as second argument
+ region_name = sys.argv[2]
+elif len(sys.argv) == 2:
+ # topic name as first argument
+ topic_name = sys.argv[1]
+ region_name = ""
+else:
+ print('Usage: ' + sys.argv[0] + ' <topic name> [region name]')
+ sys.exit(1)
+
+# endpoint and keys from vstart
+endpoint = 'http://127.0.0.1:8000'
+access_key='0555b35654ad1656d804'
+secret_key='h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+
+client = boto3.client('sns',
+ endpoint_url=endpoint,
+ aws_access_key_id=access_key,
+ region_name=region_name,
+ aws_secret_access_key=secret_key,
+ config=Config(signature_version='s3'))
+
+# to see the list of available "regions" use:
+# radosgw-admin realm zonegroup list
+
+# this is standard AWS services call, using custom attributes to add AMQP endpoint information to the topic
+
+endpoint_args = 'push-endpoint=amqp://127.0.0.1:5672&amqp-exchange=ex1&amqp-ack-level=broker'
+attributes = {nvp[0] : nvp[1] for nvp in urlparse.parse_qsl(endpoint_args, keep_blank_values=True)}
+
+print(client.create_topic(Name=topic_name, Attributes=attributes))