Skip to content

Commit

Permalink
Merge pull request #76 from lipak2345/bugfix-add-tenant-api
Browse files Browse the repository at this point in the history
Add new Tenant APIs to support Object Scale
  • Loading branch information
ben-schumacher authored Jan 20, 2021
2 parents 19e7056 + 864973d commit 74fc9a5
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ The following table shows the supported endpoints per API version.
+--------------------------+---------+---------+
| Namespace | ✓* | ✓* |
+--------------------------+---------+---------+
| Tenant(Flex) || ✓* |
+--------------------------+---------+---------+
| **Geo-Replication** |
+--------------------------+---------+---------+
| Replication Group |||
Expand Down
154 changes: 154 additions & 0 deletions ecsclient/common/multitenancy/tenant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import logging

log = logging.getLogger(__name__)

"""
Tenant APIs only supported in ECS Flex
"""


class Tenant(object):
def __init__(self, connection):
"""
Initialize a new instance
"""
self.conn = connection

def list(self):
"""
Gets the identifiers for all configured tenants.
Required role(s):
SYSTEM_ADMIN
SYSTEM_MONITOR
Example JSON result from the API:
{
u'tenant': [
{
u'id': u'tenant1'
}
]
}
"""
log.info("Getting all tenants")
return self.conn.get(url='object/tenants')

def get(self, tenant):
"""
Gets the details for the given tenant.
Required role(s):
SYSTEM_ADMIN
SYSTEM_MONITOR
TENANT_ADMIN
:param tenant: Tenant identifier for which details needs to
be retrieved.
"""
log.info("Getting info for tenant '{0}'".format(tenant))

return self.conn.get(
url='object/tenants/tenant/{0}'.format(tenant))

def create(self, account, default_data_services_vpool=None,
is_encryption_enabled=False, default_bucket_block_size=None):
"""
Creates a namespace with the given details
Required role(s):
SYSTEM_ADMIN
Example JSON result from the API:
{
'account_id':account_id,
'default_data_services_vpool': default_data_services_vpool,
'default_bucket_block_size': default_bucket_block_size
'is_encryption_enabled': is_encryption_enabled
}
"""
payload = {
'account_id': account,
'default_bucket_block_size': default_bucket_block_size,
'default_data_services_vpool': default_data_services_vpool,
'is_encryption_enabled': is_encryption_enabled,
}
log.info("Creating tenant for account '{0}'".format(account))
return self.conn.post('object/tenants/tenant', json_payload=payload)

# def update(self, tenant_id, default_data_services_vpool=None, vpools_added_to_allowed_vpools_list=[],
# vpools_added_to_disallowed_vpools_list=[], vpools_removed_from_allowed_vpools_list=[],
# vpools_removed_from_disallowed_vpools_list=[], tenant_admins=None, user_mapping=None,
# default_bucket_block_size=None, external_group_admins=None, is_encryption_enabled=None,
# is_stale_allowed=None):
# """
# Updates tenant details like replication group list, tenant admins and user mappings.
# Replication group can be:
# - Added to allowed or disallowed replication group list
# - Removed from allowed or disallowed replication group list
#
# Required role(s):
#
# SYSTEM_ADMIN
# TENANT_ADMIN
#
# There is no response body for this call
#
# Expect: HTTP/1.1 200 OK
#
# :param tenant_id: Tenant identifier whose details needs to be updated
# :param default_data_services_vpool: Default replication group identifier when creating buckets
# :param vpools_added_to_allowed_vpools_list: List of replication group identifier which will be added in the
# allowed List for allowing tenant access
# :param vpools_added_to_disallowed_vpools_list: List of replication group identifier which will be added in the
# disallowed list for prohibiting tenant access
# :param vpools_removed_from_allowed_vpools_list: List of replication group identifier which will be removed
# from allowed list
# :param vpools_removed_from_disallowed_vpools_list: List of replication group identifier which will be removed
# from disallowed list for removing their prohibition tenant access
# :param tenant_admins: Comma separated list of tenant admins
# :param user_mapping: List of user mapping objects
# :param default_bucket_block_size: Default bucket quota size
# :param external_group_admins: List of groups from AD Server
# :param is_encryption_enabled: Update encryption for the tenant. If null then encryption will not be updated.
# :param is_stale_allowed: Flag to allow stale data within the tenant. If null then stale allowance will not be
# updated
# """
# payload = {
# "default_data_services_vpool": default_data_services_vpool,
# "vpools_added_to_allowed_vpools_list": vpools_added_to_allowed_vpools_list,
# "vpools_added_to_disallowed_vpools_list": vpools_added_to_disallowed_vpools_list,
# "vpools_removed_from_allowed_vpools_list": vpools_removed_from_allowed_vpools_list,
# "vpools_removed_from_disallowed_vpools_list": vpools_removed_from_disallowed_vpools_list,
# "tenant_admins": tenant_admins,
# "user_mapping": user_mapping,
# "default_bucket_block_size": default_bucket_block_size,
# "external_group_admins": external_group_admins,
# "is_encryption_enabled": is_encryption_enabled,
# "is_stale_allowed": is_stale_allowed
# }
# # FIXME: According to the API, this call should return the updated object, but it does not
# log.info("Updating tenant ID '{}'".format(tenant_id))
# return self.conn.put('object/tenants/tenant/{}'.format(tenant_id), json_payload=payload)

def delete(self, tenant_id):
"""
Deactivates and deletes the given tenant.
Required role(s):
SYSTEM_ADMIN
There is no response body for this call
Expect: HTTP/1.1 200 OK
:param tenant_id: An active tenant identifier which needs to be deleted
"""
log.info("Deleting tenant ID '{}'".format(tenant_id))
# FIXME: This should be a DELETE request
return self.conn.post('object/tenants/tenant/{}/delete'.format(tenant_id))
27 changes: 27 additions & 0 deletions ecsclient/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,33 @@
]
}

TENANTS = {
"type": "object",
"properties": {
"tenant": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"id": {"type": "string"},
},
"required": [
"id",
]
},
}
},
"required": ["tenant"]
}
TENANT = {
"type": "object",
"properties": {
"id": {"type": "string"},
},
"required": ["id"]
}

NAMESPACE = {
"type": "object",
"properties": {
Expand Down
3 changes: 2 additions & 1 deletion ecsclient/v3/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ecsclient.v3.metering import billing
from ecsclient.v3.monitoring import capacity, dashboard, events, alerts
from ecsclient.v3.multitenancy import namespace
from ecsclient.v3.multitenancy import tenant
from ecsclient.v3.geo_replication import replication_group, temporary_failed_zone
from ecsclient.v3.provisioning import base_url, bucket, data_store, storage_pool, \
virtual_data_center, node, vdc_keystore
Expand Down Expand Up @@ -52,7 +53,7 @@ def __init__(self, *args, **kwargs):

# Multi-tenancy
self.namespace = namespace.Namespace(self)

self.tenant = tenant.Tenant(self)
# Geo-replication
self.replication_group = replication_group.ReplicationGroup(self)
self.temporary_failed_zone = temporary_failed_zone.TemporaryFailedZone(self)
Expand Down
2 changes: 2 additions & 0 deletions ecsclient/v3/multitenancy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from ecsclient.common.multitenancy import namespace
from ecsclient.common.multitenancy import tenant

namespace = namespace
tenant = tenant
4 changes: 3 additions & 1 deletion tests/functional/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def _get_config(self):
license_file = config.get('func_test', 'license_file')
with open(license_file) as f:
self.license_text = f.read()
self.override_header = config.get("func_test", 'override_header')
else:
self.skip_tests = True

Expand All @@ -38,7 +39,8 @@ def _get_client(self):
username=self.username,
password=self.password,
ecs_endpoint=self.ecs_endpoint,
token_endpoint=self.token_endpoint)
token_endpoint=self.token_endpoint,
override_header=self.override_header)

def setUp(self):
super(BaseTestCase, self).setUp()
Expand Down
58 changes: 58 additions & 0 deletions tests/functional/test_tenant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os
from ecsclient import schemas
from ecsclient.common.exceptions import ECSClientException
from tests import functional
from six.moves import configparser


class TestTenant(functional.BaseTestCase):
"""
Test conf sample
[func_test]
token_endpoint = https://10.0.1.1:4443/login
ecs_endpoint = https://10.0.1.1:4443
username = root
password = k912oz2chpsy8tny
api_version = 3
license_file = /home/user/license.lic
override_header = true
account_id = 6bd95656-42df-4e9e-9b19-b05a660eca81
"""
def __init__(self, *args, **kwargs):
super(TestTenant, self).__init__(*args, **kwargs)
config_file = os.environ.get('ECS_TEST_CONFIG_FILE',
os.path.join(os.getcwd(), "tests/test.conf"))
config = configparser.ConfigParser()
config.read(config_file)
self.config = config
if config.has_section('func_test'):
self.tenant = config.get('func_test', 'account_id')
else:
self.skip_tests = True

def setUp(self):
super(TestTenant, self).setUp()
self.create_account()
if self.skip_tests:
self.skipTest('SKIPPING FUNCTIONAL TESTS DUE TO NO CONFIG')
self.client.tenant.create(self.tenant)

def tearDown(self):
super(TestTenant, self).tearDown()
try:
self.client.tenant.delete(self.tenant)
except ECSClientException:
pass

def test_tenants_list(self):
response = self.client.tenant.list()
self.assertValidSchema(response, schemas.TENANTS)

def test_tenants_get_one(self):
response = self.client.tenant.get(self.tenant)
self.assertValidSchema(response, schemas.TENANT)

def test_tenants_delete(self):
self.client.tenant.delete(self.tenant)
f = self.client.tenant.get
self.assertRaises(ECSClientException, f, self.tenant)

0 comments on commit 74fc9a5

Please sign in to comment.