From 1ac8c822942d6ba76d899a5042fc23f088dd0170 Mon Sep 17 00:00:00 2001 From: Bala FA Date: Fri, 12 Feb 2021 11:03:29 +0530 Subject: [PATCH] Add MinIO admin API support. (#1076) --- minio/__init__.py | 1 + minio/minioadmin.py | 281 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 minio/minioadmin.py diff --git a/minio/__init__.py b/minio/__init__.py index a1d558219..3fd76ee34 100644 --- a/minio/__init__.py +++ b/minio/__init__.py @@ -44,3 +44,4 @@ # pylint: disable=unused-import from .api import Minio from .error import InvalidResponseError, S3Error, ServerError +from .minioadmin import MinioAdmin diff --git a/minio/minioadmin.py b/minio/minioadmin.py new file mode 100644 index 000000000..d3fdb1c68 --- /dev/null +++ b/minio/minioadmin.py @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- +# MinIO Python Library for Amazon S3 Compatible Cloud Storage, +# (C) 2021 MinIO, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file 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. + +# pylint: disable=too-many-public-methods + +"""MinIO Admin wrapper using MinIO Client (mc) tool.""" + +from __future__ import absolute_import + +import json +import subprocess + + +class MinioAdmin: + """MinIO Admin wrapper using MinIO Client (mc) tool.""" + + def __init__( + self, target, + binary_path=None, config_dir=None, ignore_cert_check=False, + timeout=None, + ): + self._target = target + self._timeout = timeout + self._base_args = [binary_path or "mc", "--json"] + if config_dir: + self._base_args += ["--config-dir", config_dir] + if ignore_cert_check: + self._base_args.append("--insecure") + self._base_args.append("admin") + + def _run(self, args, multiline=False): + """Execute mc command and return JSON output.""" + proc = subprocess.run( + self._base_args + args, + capture_output=True, + timeout=self._timeout, + check=True, + ) + if multiline: + return [json.loads(line) for line in proc.stdout.split("\n")] + return json.loads(proc.stdout) + + def service_restart(self): + """Restart MinIO service.""" + return self._run(["service", "restart", self._target]) + + def service_stop(self): + """Stop MinIO service.""" + return self._run(["service", "stop", self._target]) + + def update(self): + """Update MinIO.""" + return self._run(["update", self._target]) + + def info(self): + """Get MinIO server information.""" + return self._run(["info", self._target]) + + def user_add(self, access_key, secret_key): + """Add a new user.""" + return self._run(["user", "add", self._target, access_key, secret_key]) + + def user_disable(self, access_key): + """Disable user.""" + return self._run(["user", "disable", self._target, access_key]) + + def user_enable(self, access_key): + """Enable user.""" + return self._run(["user", "enable", self._target, access_key]) + + def user_remove(self, access_key): + """Remove user.""" + return self._run(["user", "remove", self._target, access_key]) + + def user_info(self, access_key): + """Get user information.""" + return self._run(["user", "info", self._target, access_key]) + + def user_list(self): + """List users.""" + return self._run(["user", "list", self._target], multiline=True) + + def group_add(self, group_name, members): + """Add users a new or existing group.""" + return self._run(["group", "add", self._target, group_name] + members) + + def group_disable(self, group_name): + """Disable group.""" + return self._run(["group", "disable", self._target, group_name]) + + def group_enable(self, group_name): + """Enable group.""" + return self._run(["group", "enable", self._target, group_name]) + + def group_remove(self, group_name, members=None): + """Remove group or members from a group.""" + return self._run( + ["group", "remove", self._target, group_name] + (members or []), + ) + + def group_info(self, group_name): + """Get group information.""" + return self._run(["group", "info", self._target, group_name]) + + def group_list(self): + """List groups.""" + return self._run(["group", "list", self._target], multiline=True) + + def policy_add(self, policy_name, policy_file): + """Add new policy.""" + return self._run( + ["policy", "add", self._target, policy_name, policy_file], + ) + + def policy_remove(self, policy_name): + """Remove policy.""" + return self._run(["policy", "remove", self._target, policy_name]) + + def policy_info(self, policy_name): + """Get policy information.""" + return self._run(["policy", "info", self._target, policy_name]) + + def policy_list(self): + """List policies.""" + return self._run(["policy", "list", self._target], multiline=True) + + def policy_set(self, policy_name, user=None, group=None): + """Set IAM policy on a user or group.""" + if (user is not None) ^ (group is not None): + return self._run( + [ + "policy", "set", self._target, policy_name, + ("user=" if user else "group=") + (user or group), + ], + ) + raise ValueError("either user or group must be set") + + def config_get(self, key=None): + """Get configuration parameters.""" + return self._run( + ["config", "get", self._target] + [key] if key else [], + key, + ) + + def config_set(self, key, config): + """Set configuration parameters.""" + args = [name + "=" + value for name, value in config.items()] + return self._run(["config", "set", self._target, key] + args) + + def config_reset(self, key, name=None): + """Reset configuration parameters.""" + if name: + key += ":" + name + return self._run(["config", "reset", self._target, key]) + + def config_remove(self, access_key): + """Remove config.""" + return self._run(["config", "remove", self._target, access_key]) + + def config_history(self): + """Get historic configuration changes.""" + return self._run(["config", "history", self._target], multiline=True) + + def config_restore(self, restore_id): + """Restore to a specific configuration history.""" + return self._run(["config", "restore", self._target, restore_id]) + + def profile_start(self, profilers=()): + """Start recording profile data.""" + args = ["profile", "start"] + if profilers: + args += ["--type", ",".join(profilers)] + args.append(self._target) + return self._run(args) + + def profile_stop(self): + """Stop and download profile data.""" + return self._run(["profile", "stop", self._target]) + + def top_locks(self): + """Get a list of the 10 oldest locks on a MinIO cluster.""" + return self._run(["top", "locks", self._target], multiline=True) + + def prometheus_generate(self): + """Generate prometheus configuration.""" + return self._run(["prometheus", "generate", self._target]) + + def kms_key_create(self, key=None): + """Create a new KMS master key.""" + return self._run( + [ + "kms", "key", "create", self._target, key + ] + ([key] if key else []), + ) + + def kms_key_status(self, key=None): + """Get status information of a KMS master key.""" + return self._run( + [ + "kms", "key", "status", self._target, key + ] + ([key] if key else []), + ) + + def bucket_remote_add( + self, src_bucket, dest_url, + path=None, region=None, bandwidth=None, service=None, + ): + """Add a new remote target.""" + args = [ + "bucket", "remote", "add", self._target + "/" + src_bucket, + dest_url, "--service", service or "replication", + ] + if path: + args += ["--path", path] + if region: + args += ["--region", region] + if bandwidth: + args += ["--bandwidth", bandwidth] + return self._run(args) + + def bucket_remote_edit(self, src_bucket, dest_url, arn): + """Edit credentials of remote target.""" + return self._run( + [ + "bucket", "remote", "edit", self._target + "/" + src_bucket, + dest_url, "--arn", arn, + ], + ) + + def bucket_remote_list(self, src_bucket=None, service=None): + """List remote targets.""" + return self._run( + [ + "bucket", "remote", "ls", + self._target + ("/" + src_bucket if src_bucket else ""), + "--service", service or "replication", + ], + ) + + def bucket_remote_remove(self, src_bucket, arn): + """Remove configured remote target.""" + return self._run( + [ + "bucket", "remote", "rm", self._target + "/" + src_bucket, + "--arn", arn, + ], + ) + + def bucket_quota_set(self, bucket, fifo=None, hard=None): + """Set bucket quota configuration.""" + if fifo is None and hard is None: + raise ValueError("fifo or hard must be set") + args = ["bucket", "quota", self._target + "/" + bucket] + if fifo: + args += ["--fifo", fifo] + if hard: + args += ["--hard", hard] + return self._run(args) + + def bucket_quota_clear(self, bucket): + """Clear bucket quota configuration.""" + return self._run( + ["bucket", "quota", self._target + "/" + bucket, "--clear"], + ) + + def bucket_quota_get(self, bucket): + """Get bucket quota configuration.""" + return self._run(["bucket", "quota", self._target + "/" + bucket])