aboutsummaryrefslogtreecommitdiffstats
path: root/rados/rgwa_client.py
diff options
context:
space:
mode:
Diffstat (limited to 'rados/rgwa_client.py')
-rw-r--r--rados/rgwa_client.py627
1 files changed, 0 insertions, 627 deletions
diff --git a/rados/rgwa_client.py b/rados/rgwa_client.py
deleted file mode 100644
index 23715ec..0000000
--- a/rados/rgwa_client.py
+++ /dev/null
@@ -1,627 +0,0 @@
-
-# ============LICENSE_START==========================================
-# org.onap.vvp/test-engine
-# ===================================================================
-# Copyright © 2017 AT&T Intellectual Property. All rights reserved.
-# ===================================================================
-#
-# Unless otherwise specified, all software contained herein is licensed
-# under the Apache License, Version 2.0 (the “License”);
-# you may not use this software except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-#
-#
-# Unless otherwise specified, all documentation contained herein is licensed
-# under the Creative Commons License, Attribution 4.0 Intl. (the “License”);
-# you may not use this documentation except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://creativecommons.org/licenses/by/4.0/
-#
-# Unless required by applicable law or agreed to in writing, documentation
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# ============LICENSE_END============================================
-#
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-"""A Ceph Rados Gateway Admin Operations API client."""
-
-# Design goals:
-#
-# - Minimal abstractions over the raw Requests calls
-# - Python method signatures enforce optional/required API parameters
-# - DRY by procedurally mapping kwargs to API parameters
-# - (TODO) procedurally generate this library directly from Ceph docs
-
-import os
-
-from awsauth import S3Auth
-from requests import request
-
-
-def _validate_args(valid_args, **kwargs):
- """Validate kwargs conforms to a specification of allowable values.
-
- Ensures that any keyword arguments either:
- - are unconstrained by valid_args, or
- - have a value of None, or
- - have a value that matches one of the corresponding specified values.
-
- This is useful for limiting several common keyword arguments to a set of
- values across many methods, while ignoring those set to None. (Typically,
- these are optional and were unspecified by the caller.)
-
- This is a validator function: it either returns None on success, or raises
- an exception on failure.
-
- """
- for keyword, value in kwargs.items():
- if keyword not in valid_args:
- continue
- if value is None:
- continue
- if value in valid_args[keyword]:
- continue
- raise ValueError(
- "Invalid parameter {:s}={!r}; must be one of: {!r}".format(
- keyword, value, valid_args[keyword]))
-
-
-class RGWAClient(object):
- """A client for the Ceph Rados Gateway Admin Operations API.
-
- This class is implemented as a simplistic/mechanical wrapper around the
- Python Requests library. Calling its methods triggers HTTP(S) calls to the
- specified API endpoint, and the responses are decoded from JSON to Python
- objects before being returned.
-
- The methods available on this object should mirror the endpoints of the API
- closely enough that its documentation may be used as a reference:
-
- http://docs.ceph.com/docs/master/radosgw/adminops/
-
- """
- valid_args = {
- 'quota_type': ['user', 'bucket'],
- 'key_type': ['s3', 'swift'],
- },
-
- def __init__(
- self,
- base_url,
- access_key=None,
- secret_key=None,
- verify='/opt/secrets/site-crt/site.crt',
- return_raw_response=False):
- """
-
- base_url (string):
-
- The full URL to your admin entry point. Should include the protocol
- ("http://" or "https://"), and optionally the port as well. The
- URL-path to the admin entry point is configurable using "rgw admin
- entry" in your Ceph configuration. Example:
-
- "https://s3.example.com:8080/admin"
-
- access_key (string): Your AWS Access Key ID
- secret_key (string): Your AWS Secret Access Key
-
- If either of access_key or secret_key are omitted, this class will
- attempt to look the values in the environment variables
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY respectively.
-
- verify (boolean):
-
- Set to False to disable SSL Certificate verification, or optionally
- set to the path to a CA Certificate bundle. This is passed directly
- to the underlying call to the requests library; see:
- http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification
-
- return_raw_response (boolean):
-
- All of the methods of this class return, upon success, the objects
- resulting from parsing the JSON data returned by the API. On error,
- they raise an exception. This is meant for caller convenience, but
- may be undesirable in some situations because callers have no
- access to the additional data and methods available in the raw
- Response object.
-
- from ice_rgwa_client import (
- RGWAClient, HTTPError)
- # ...
- rgw = RGWAClient(
- access_key='...',
- secret_key='...',
- base_url='...',
- )
- # ...
- try:
- user = rgw.get_user('nonexistent')
- except HTTPError as exc:
- if exc.response.status_code == 404:
- print("No such user")
- continue
- else:
- print("Problem loading user")
- raise
-
- If return_raw_response is set to True, the methods will instead
- return the raw Response object from the Requests library, and it
- will be up to the caller to check the error status as needed.
-
- See
- http://docs.python-requests.org/en/master/user/quickstart/#json-response-content
-
- from ice_rgwa_client import RGWAClient
- # ...
- rgw = RGWAClient(
- access_key='...',
- secret_key='...',
- base_url='...',
- return_raw_response=True,
- )
- # ...
- response = rgw.get_user('nonexistent')
- if response.status_code == 404:
- print("No such user")
- elif response.status_code != 200:
- print("Problem loading user")
- else:
- user = response.json()
-
- """
- if not access_key:
- access_key = os.environ.get('AWS_ACCESS_KEY_ID')
- if not secret_key:
- secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
- self.base_url = base_url
- self.verify = verify
- self.auth = S3Auth(access_key, secret_key, service_url=base_url)
- self.return_raw_response = return_raw_response
-
- def _request(self, method, endpoint, action=None, data=None, **kwargs):
- """Helper method to factor out actions common to Ceph Rados Gateway
- Admin requests.
-
- "data" is a dictionary that, if provided, will be JSON-encoded and
- submitted in the body of the request.
-
- Other keyword arguments will be encoded and used as URL parameters.
-
- "_" in kwargs will be converted to "-" in URL parameter keys.
-
- """
- # We can validate some arguments before the round trip to the server
- _validate_args(self.valid_args, **kwargs)
-
- # We never want to pass literal None to the API, so we can use None to
- # indicate "do not use this k/v pair at all." Using this, optional
- # parameters still appear in the function signature but will be omitted
- # from the request when unspecified.
- params = {
- k.replace('_', '-'): v
- for k, v in kwargs.items()
- if v is not None}
-
- # Same for body data but without _/- conversion...
- data = {} if data is None else {
- k: v
- for k, v in data.items()
- if v is not None}
-
- # The Ceph Object Gateway Admin Operations API specifies, for some
- # operations, a key-only URL parameter (that we call "action") with no
- # associated value. For simplicity, we violate the spec slightly by
- # assigning it a value of "". It seems to work.
- if action:
- params[action] = ''
-
- # JSON output is the default, so there's no need to specify format=json
- # parameter despite all the examples in the docs making it explicit.
-
- url = '%s/%s' % (self.base_url, endpoint)
- response = request(
- method=method,
- url=url,
- params=params,
- json=data,
- auth=self.auth,
- verify=self.verify,
- )
- if self.return_raw_response:
- return response
- else:
- if response.status_code == 404:
- return None
-
- response.raise_for_status()
- try:
- return response.json()
- except ValueError:
- # At this point we have a successful 200 status but a problem
- # decoding the json. Some responses are empty:
- if not response.content:
- return {}
- raise
-
- #
- # These methods appear in the same order as the corresponding endpoints in
- # the documentation. The docstrings are copied verbatim from that
- # documentation. See:
- # http://docs.ceph.com/docs/master/radosgw/adminops/
- #
-
- def get_usage(
- self,
- uid=None,
- start=None,
- end=None,
- show_entries=False,
- show_summary=False):
- """Request bandwidth usage information.
-
- Note: this feature is disabled by default, can be enabled by setting
- 'rgw enable usage log = true' in the appropriate section of ceph.conf.
- For changes in ceph.conf to take effect, radosgw process restart is
- needed.
-
- """
- return self._request(
- 'get', 'usage',
- uid=uid,
- start=start,
- end=end,
- show_entries=show_entries,
- show_summary=show_summary,
- )
-
- def trim_usage(self, uid=None, start=None, end=None, remove_all=False):
- """Remove usage information.
-
- With no dates specified, removes all usage information.
-
- Note: this feature is disabled by default, can be enabled by setting
- 'rgw enable usage log = true' in the appropriate section of ceph.conf.
- For changes in ceph.conf to take effect, radosgw process restart is
- needed.
-
- """
- return self._request(
- 'delete', 'usage',
- uid=uid,
- start=start,
- end=end,
- remove_all=remove_all,
- )
-
- def get_user(self, uid):
- """Get user information."""
- return self._request('get', 'user', uid=uid)
-
- def create_user(self, uid, display_name, email=None, key_type='s3',
- access_key=None, secret_key=None, user_caps=None,
- generate_key=True, max_buckets=None, suspended=False):
- """Create a new user.
-
- By default, a S3 key pair will be created automatically and returned in
- the response. If only one of access_key or secret_key is provided, the
- omitted key will be automatically generated. By default, a generated
- key is added to the keyring without replacing an existing key pair. If
- access_key is specified and refers to an existing key owned by the user
- then it will be modified.
-
- """
- return self._request(
- 'put', 'user',
- uid=uid,
- display_name=display_name,
- email=email,
- key_type=key_type,
- access_key=access_key,
- secret_key=secret_key,
- user_caps=user_caps,
- generate_key=generate_key,
- max_buckets=max_buckets,
- suspended=suspended,
- )
-
- def modify_user(self, uid, display_name=None, email=None, key_type='s3',
- access_key=None, secret_key=None, user_caps=None,
- generate_key=True, max_buckets=None, suspended=False):
- """Modify a user."""
- return self._request(
- 'post', 'user',
- uid=uid,
- display_name=display_name,
- email=email,
- key_type=key_type,
- access_key=access_key,
- secret_key=secret_key,
- user_caps=user_caps,
- generate_key=generate_key,
- max_buckets=max_buckets,
- suspended=suspended,
- )
-
- def remove_user(self, uid, purge_data=False):
- """Remove an existing user."""
- return self._request(
- 'delete', 'user',
- uid=uid,
- purge_data=purge_data,
- )
-
- def create_subuser(
- self,
- uid,
- subuser=None,
- secret_key=None,
- access_key=None,
- key_type=None,
- access=None,
- generate_secret=False):
- """Create a new subuser.
-
- (Primarily useful for clients using the Swift API). Note that in
- general for a subuser to be useful, it must be granted permissions by
- specifying access. As with user creation if subuser is specified
- without secret, then a secret key will be automatically generated.
-
- """
- return self._request(
- 'put', 'user', 'subuser',
- uid=uid,
- subuser=subuser,
- secret_key=secret_key,
- access_key=access_key,
- key_type=key_type,
- access=access,
- generate_secret=generate_secret,
- )
-
- def modify_subuser(
- self,
- uid,
- subuser,
- secret=None,
- key_type='swift',
- access=None,
- generate_secret=False):
- """Modify an existing subuser."""
- return self._request(
- 'post', 'user', 'subuser',
- uid=uid,
- subuser=subuser,
- secret=secret,
- key_type=key_type,
- access=access,
- generate_secret=generate_secret,
- )
-
- def remove_subuser(self, uid, subuser, purge_keys=True):
- """Remove an existing subuser."""
- return self._request(
- 'delete', 'user', 'subuser',
- uid=uid,
- subuser=subuser,
- purge_keys=purge_keys,
- )
-
- def create_key(self, uid, subuser=None, key_type='s3', access_key=None,
- secret_key=None, generate_key=True):
- """Create a new key.
-
- If a subuser is specified then by default created keys will be swift
- type. If only one of access_key or secret_key is provided the committed
- key will be automatically generated, that is if only secret_key is
- specified then access_key will be automatically generated. By default,
- a generated key is added to the keyring without replacing an existing
- key pair. If access_key is specified and refers to an existing key
- owned by the user then it will be modified. The response is a container
- listing all keys of the same type as the key created. Note that when
- creating a swift key, specifying the option access_key will have no
- effect. Additionally, only one swift key may be held by each user or
- subuser.
-
- """
- return self._request(
- 'put', 'user', 'key',
- uid=uid,
- subuser=subuser,
- key_type=key_type,
- access_key=access_key,
- secret_key=secret_key,
- generate_key=generate_key,
- )
-
- def remove_key(self, access_key, key_type=None, uid=None, subuser=None):
- """Remove an existing key."""
- return self._request(
- 'delete', 'user', 'key',
- access_key=access_key,
- key_type=key_type,
- uid=uid,
- subuser=subuser,
- )
-
- def get_bucket(self, bucket=None, uid=None, stats=False):
- """Get information about a subset of the existing buckets.
-
- If uid is specified without bucket then all buckets beloning to the
- user will be returned. If bucket alone is specified, information for
- that particular bucket will be retrieved.
-
- """
- return self._request(
- 'get', 'bucket',
- bucket=bucket,
- uid=uid,
- stats=stats,
- )
-
- def check_bucket_index(self, bucket, check_objects=False, fix=False):
- """Check the index of an existing bucket.
-
- NOTE: to check multipart object accounting with check-objects, fix must
- be set to True.
-
- """
- return self._request(
- 'get', 'bucket', 'index',
- bucket=bucket,
- check_objects=check_objects,
- fix=fix,
- )
-
- def remove_bucket(self, bucket, purge_objects=False):
- """Delete an existing bucket."""
- return self._request(
- 'delete', 'bucket',
- bucket=bucket,
- purge_objects=purge_objects,
- )
-
- def unlink_bucket(self, bucket, uid):
- """Unlink a bucket from a specified user.
-
- Primarily useful for changing bucket ownership.
-
- """
- return self._request(
- 'post', 'bucket',
- bucket=bucket,
- uid=uid,
- )
-
- def link_bucket(self, bucket, bucket_id, uid):
- """Link a bucket to a specified user, unlinking the bucket from any
- previous user.
-
- """
- # Both bucket and bucket_id are really required. Use get_bucket() to
- # discover the id of a bucket from its name.
- #
- # FIXME: add a convenience method to look up the id?
- return self._request(
- 'put', 'bucket',
- bucket=bucket,
- bucket_id=bucket_id,
- uid=uid,
- )
-
- def remove_object(self, bucket, object_name):
- """Remove an existing object.
-
- NOTE: Does not require owner to be non-suspended.
-
- """
- return self._request(
- 'delete', 'bucket', 'object',
- bucket=bucket,
- object_name=object_name,
- )
-
- def get_policy(self, bucket, object_name=None):
- """Read the policy of an object or bucket."""
- return self._request(
- 'get', 'bucket', 'policy',
- bucket=bucket,
- object_name=object_name,
- )
-
- def add_capability(self, uid, user_caps):
- """Add an administrative capability to a specified user.
-
- uid (string):
- The user ID to add an administrative capability to.
-
- user_caps (string):
- The administrative capability to add to the user. Example:
- "usage=read,write;user=write"
-
- """
- return self._request(
- 'put', 'user', 'caps',
- uid=uid,
- user_caps=user_caps,
- )
-
- def remove_capability(self, uid, user_caps):
- """Remove an administrative capability from a specified user."""
- return self._request(
- 'delete', 'user', 'caps',
- uid=uid,
- user_caps=user_caps,
- )
-
- def get_quota(self, uid, quota_type):
- return self._request(
- 'get', 'user', 'quota',
- uid=uid,
- quota_type=quota_type,
- )
-
- def set_quota(self, uid, quota_type, bucket=None, max_size_kb=None,
- max_objects=None, enabled=None):
- return self._request(
- 'put', 'user', 'quota',
- quota_type=quota_type,
- uid=uid,
- bucket=bucket,
- max_size_kb=max_size_kb,
- max_objects=max_objects,
- enabled=enabled,
- )
-
- #
- # Convenience methods
- #
-
- def get_user_quota(self, uid):
- return self.get_quota(uid=uid, quota_type='user')
-
- def set_user_quota(
- self,
- uid,
- max_size_kb=None,
- max_objects=None,
- enabled=None):
- return self.set_quota(
- uid=uid,
- quota_type='user',
- max_size_kb=max_size_kb,
- max_objects=max_objects,
- enabled=enabled,
- )
-
- def get_user_bucket_quota(self, uid):
- return self.get_quota(uid=uid, quota_type='bucket')
-
- def set_user_bucket_quota(
- self,
- uid,
- bucket,
- max_size_kb=None,
- max_objects=None,
- enabled=None):
- return self.set_quota(
- uid=uid,
- bucket=bucket,
- quota_type='bucket',
- max_size_kb=max_size_kb,
- max_objects=max_objects,
- enabled=enabled,
- )