For a higher level understanding of multi-tenancy architecture, see Multi-Tenancy feature.
All Multi-Tenancy subcommands are located under CLI ozone tenant
.
The commands below assume a Kerberized Ozone cluster with Ranger install. Enabling HTTPS on S3 Gateway is optional but recommended.
The exit code of a successful tenant command should be 0
.
A non-zero exit code indicates failure, which should be accompanied an error message.
Follow the Multi-Tenancy Setup guide if you haven’t done so.
If the OzoneManagers are running in HA, append --om-service-id=
accordingly to the commands.
Create a new tenant in the current Ozone cluster. This operation requires Ozone cluster administrator privilege.
Apart from adding new OM DB entries, creating a tenant also does the following in the background:
tenantName-UserRole
and tenantName-AdminRole
.ozone tenant [--verbose] create <TENANT_NAME>
Example:
bash-4.2$ kinit -kt /etc/security/keytabs/om.keytab om/om@EXAMPLE.COM
bash-4.2$ ozone tenant create tenantone
2022-02-16 00:00:00,000 [main] INFO rpc.RpcClient: Creating Tenant: 'tenantone', with new volume: 'tenantone'
Verbose output example:
bash-4.2$ kinit -kt /etc/security/keytabs/om.keytab om/om@EXAMPLE.COM
bash-4.2$ ozone tenant --verbose create tenantone
2022-02-16 00:00:00,000 [main] INFO rpc.RpcClient: Creating Tenant: 'tenantone', with new volume: 'tenantone'
{
"tenantId": "tenantone"
}
List all tenants in an Ozone cluster. Optionally, use --json
to print the detailed result in JSON.
ozone tenant list [--json]
Example:
bash-4.2$ ozone tenant list
tenantone
bash-4.2$ ozone tenant list --json
[
{
"tenantId": "tenantone",
"bucketNamespaceName": "tenantone",
"userRoleName": "tenantone-UserRole",
"adminRoleName": "tenantone-AdminRole",
"bucketNamespacePolicyName": "tenantone-VolumeAccess",
"bucketPolicyName": "tenantone-BucketAccess"
}
]
The first user in a tenant must be assigned by an Ozone cluster administrator.
By default when user testuser
is assigned to tenant tenantone
, the generated Access ID for the user in this tenant is tenantone$testuser
.
It is possible to assign a user to multiple tenants.
ozone tenant [--verbose] user assign <USER_NAME> --tenant=<TENANT_NAME>
<USER_NAME>
should be a short user name for a Kerberos principal, e.g. testuser
when the Kerberos principal is testuser/scm@EXAMPLE.COM
Example:
bash-4.2$ ozone tenant user assign testuser --tenant=tenantone
export AWS_ACCESS_KEY_ID='tenantone$testuser'
export AWS_SECRET_ACCESS_KEY='<GENERATED_SECRET>'
bash-4.2$ ozone tenant user assign testuser --tenant=tenantone
TENANT_USER_ACCESS_ID_ALREADY_EXISTS accessId 'tenantone$testuser' already exists!
bash-4.2$ ozone tenant --verbose user assign testuser2 --tenant=tenantone
export AWS_ACCESS_KEY_ID='tenantone$testuser2'
export AWS_SECRET_ACCESS_KEY='<GENERATED_SECRET>'
Assigned 'testuser2' to 'tenantone' with accessId 'tenantone$testuser2'.
The first user in a tenant must be assigned by an Ozone cluster administrator.
Both delegated and non-delegated tenant admin can assign and revoke regular tenant users.
The only difference between delegated tenant admin and non-delegated tenant admin is that delegated tenant admin can assign and revoke tenant admins in the tenant, while non-delegated tenant admin can’t.
By default, ozone tenant assignadmin
assigns a non-delegated tenant admin.
To assign a delegated tenant admin, specify --delegated
or -d
.
It is possible to assign a user to be tenant admins in multiple tenants. Just a reminder, the user would have a different access ID under each tenant.
ozone tenant user assignadmin <ACCESS_ID> [-d|--delegated] --tenant=<TENANT_NAME>
Example:
bash-4.2$ ozone tenant user assignadmin 'tenantone$testuser' --tenant=tenantone
By default, if the command succeeds, it exits with 0
and prints nothing. Use --verbose
to print the result in JSON.
bash-4.2$ ozone tenant --verbose user assignadmin 'tenantone$testuser' --tenant=tenantone
{
"accessId": "tenantone$testuser",
"tenantId": "tenantone",
"isAdmin": true,
"isDelegatedAdmin": true
}
Once testuser
becomes a tenant admin of tenantone
, one can kinit as testuser
and assign new users to the tenant,
even new tenant admins (if delegated). Example commands for illustration:
kinit -kt /etc/security/keytabs/testuser.keytab testuser/scm@EXAMPLE.COM
ozone tenant user assign testuser2 --tenant=tenantone
ozone tenant user assignadmin 'tenantone$testuser2' --tenant=tenantone
ozone tenant user list [--json] <TENANT_NAME>
Example:
bash-4.2$ ozone tenant user list tenantone
- User 'testuser' with accessId 'tenantone$testuser'
- User 'testuser2' with accessId 'tenantone$testuser2'
bash-4.2$ ozone tenant user list --json tenantone
[
{
"user": "testuser",
"accessId": "tenantone$testuser"
},
{
"user": "testuser2",
"accessId": "tenantone$testuser2"
}
]
This command lists all tenants a user is assigned to.
ozone tenant user info [--json] <USER_NAME>
Example:
bash-4.2$ ozone tenant user info testuser
User 'testuser' is assigned to:
- Tenant 'tenantone' delegated admin with accessId 'tenantone$testuser'
bash-4.2$ ozone tenant user info --json testuser
{
"user": "testuser",
"tenants": [
{
"accessId": "tenantone$testuser",
"tenantId": "tenantone",
"isAdmin": true,
"isDelegatedAdmin": true
}
]
}
ozone tenant [--verbose] user revokeadmin <ACCESS_ID>
Example:
bash-4.2$ ozone tenant user revokeadmin 'tenantone$testuser'
bash-4.2$ ozone tenant --verbose user revokeadmin 'tenantone$testuser'
{
"accessId": "tenantone$testuser",
"isAdmin": false,
"isDelegatedAdmin": false
}
ozone tenant [--verbose] user revoke <ACCESS_ID>
Example:
bash-4.2$ ozone tenant user revoke 'tenantone$testuser'
With verbose output:
bash-4.2$ ozone tenant --verbose user revoke 'tenantone$testuser'
Revoked accessId 'tenantone$testuser'.
In order to be able to delete a tenant, the tenant has to be empty. i.e. All users need to be revoked before a tenant can be deleted.
Otherwise OM will throw TENANT_NOT_EMPTY
exception and refuse to delete the tenant.
Note that it is intentional by design that the volume created and associated with the tenant during tenant creation is not removed. An admin has to remove the volume manually as prompt in the CLI, if deemed necessary.
Verbose option, in addition, will print the Ozone Manager RAW response in JSON.
ozone tenant [--verbose] delete <TENANT_NAME>
Example:
bash-4.2$ ozone tenant delete tenantone
Deleted tenant 'tenantone'.
But the associated volume 'tenantone' is not removed. To delete it, run
ozone sh volume delete tenantone
With verbose output:
bash-4.2$ ozone tenant --verbose delete tenantone
Deleted tenant 'tenantone'.
But the associated volume 'tenantone' is not removed. To delete it, run
ozone sh volume delete tenantone
{
"tenantId": "tenantone",
"volumeName": "tenantone",
"volumeRefCount": 0
}
If an Ozone cluster admin (or whoever has the permission to delete the volume in Ranger) tries delete a volume before the tenant is deleted using the command above,
the ozone sh volume delete
command would fail because the volume reference count is not zero:
bash-4.2$ ozone sh volume delete tenantone
VOLUME_IS_REFERENCED Volume reference count is not zero (1). Ozone features are enabled on this volume. Try `ozone tenant delete <tenantId>` first.
Bucket links can be used to allow access to buckets outside of the tenant volume.
Bucket (sym)links are a special type of bucket that points to other buckets in the same Ozone cluster. It is similar to POSIX symbolic links.
An example to create a bucket link:
$ ozone tenant linkbucket /vol1/bucket1 /tenantone/linked-bucket1
The command above creates a bucket symlink linked-bucket1
in volume tenantone
, which points to bucket1
in vol1
.
As long as the user running this command has the permission to create a bucket in the target volume tenantone
, the command will succeed.
tenantone
access the bucket, a new policy should be added by a Ranger admin that allow that user intended permissions (READ, WRITE, LIST, CREATE, DELETE, ...
) to the source bucket bucket1
in volume vol1
.ozone tenant linkbucket
command is equivalent to ozone sh bucket link
command (see Expose any volume section in S3 protocol).Here is an example of accessing the bucket using AWS CLI in the Docker Compose cluster, with tenant tenantone
created and testuser
assigned to the tenant.
bash-4.2$ aws configure
AWS Access Key ID [****************fslf]: tenantone$testuser
AWS Secret Access Key [****************fslf]: <GENERATED_SECRET>
Default region name [us-west-1]:
Default output format [None]:
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 list-buckets
{
"Buckets": []
}
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 create-bucket --bucket bucket-test1
{
"Location": "http://s3g:9878/bucket-test1"
}
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 list-buckets
{
"Buckets": [
{
"Name": "bucket-test1",
"CreationDate": "2022-02-16T00:05:00.000Z"
}
]
}
In the Docker Compose cluster, the AWS CLI might report AccessDenied
because it uses a mocked Ranger endpoint (which can’t be used to perform authorization). A production Ranger setup uses RangerOzoneAuthorizer
in OM for authorization while the ozonesecure
Docker Compose cluster still uses OzoneNativeAuthorizer
. So a workaround is to set the volume owner to testuser
to gain full access, as OzoneNativeAuthorizer
grants the volume owner full permission:
ozone sh volume update tenantone --user=testuser
The bucket created with aws s3api
is also visible under Ozone CLI:
bash-4.2$ ozone sh bucket list /tenantone
[ {
"metadata" : { },
"volumeName" : "tenantone",
"name" : "bucket-test1",
"storageType" : "DISK",
"versioning" : false,
"usedBytes" : 0,
"usedNamespace" : 0,
"creationTime" : "2022-02-16T00:05:00.000Z",
"modificationTime" : "2022-02-16T00:05:00.000Z",
"quotaInBytes" : -1,
"quotaInNamespace" : -1,
"bucketLayout" : "OBJECT_STORE",
"owner" : "root",
"link" : false
} ]
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 put-object --bucket bucket-test1 --key file1 --body README.md
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 list-objects --bucket bucket-test1
{
"Contents": [
{
"Key": "file1",
"LastModified": "2022-02-16T00:10:00.000Z",
"ETag": "2022-02-16T00:10:00.000Z",
"Size": 3811,
"StorageClass": "STANDARD"
}
]
}
bash-4.2$ aws s3api --endpoint-url http://s3g:9878 get-object --bucket bucket-test1 --key file1 file1-get.txt
{
"AcceptRanges": "bytes",
"LastModified": "Wed, 16 Feb 2022 00:10:00 GMT",
"ContentLength": 3811,
"CacheControl": "no-cache",
"ContentType": "application/octet-stream",
"Expires": "Wed, 16 Feb 2022 00:15:00 GMT",
"Metadata": {}
}
bash-4.2$ diff file1-get.txt README.md