From 24d54bbd328a163fce9b0acbcff6cc1612f48f5d Mon Sep 17 00:00:00 2001 From: nameczz Date: Fri, 29 Jul 2022 09:38:42 +0800 Subject: [PATCH 1/4] add user apis and update setup.py Signed-off-by: nameczz --- milvus_cli/scripts/milvus_cli.py | 56 ++++++++++++++++++++++ milvus_cli/utils.py | 79 +++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 21 deletions(-) diff --git a/milvus_cli/scripts/milvus_cli.py b/milvus_cli/scripts/milvus_cli.py index 695b2fc..a7d533e 100644 --- a/milvus_cli/scripts/milvus_cli.py +++ b/milvus_cli/scripts/milvus_cli.py @@ -424,6 +424,16 @@ def indexes(obj, collection): except Exception as e: click.echo(message=e, err=True) +@listDetails.command() +@click.pass_obj +def users(obj): + """List all users in Milvus""" + try: + obj.checkConnection() + click.echo(obj.listCredUsers()) + except Exception as e: + click.echo(message=e, err=True) + @cli.group("describe", no_args_is_help=False) @click.pass_obj @@ -716,6 +726,27 @@ def createIndex(obj): click.echo("Create index successfully!") +@createDetails.command("user") +@click.option("-u", "--username", "username", help="The username of milvus user.") +@click.option("-p", "--password", "password", help="The pawssord of milvus user.") +@click.pass_obj +def createUser(obj, username, password): + """ + Create user. + + Example: + + milvus_cli > create user -u zilliz -p zilliz + """ + try: + obj.checkConnection() + click.echo(obj.createCredUser(username,password)) + click.echo("Create user successfully") + except Exception as e: + click.echo(message=e, err=True) + + + @cli.group("delete", no_args_is_help=False) @click.pass_obj def deleteObject(obj): @@ -868,6 +899,31 @@ def deleteIndex(obj, collectionName, timeout): ) +@deleteObject.command("user") +@click.option("-u", "--username", "username", help="The username of milvus user") +@click.pass_obj +def deleteUser(obj, username): + """ + Drop user in milvus by username + + Example: + + milvus_cli > delete user -u zilliz + """ + click.echo( + "Warning!\nYou are trying to delete the user in milvus. This action cannot be undone!\n" + ) + if not click.confirm("Do you want to continue?"): + return + try: + obj.checkConnection() + result = obj.deleteCredUser(username) + click.echo("Drop user successfully!") + click.echo(result) + except Exception as e: + click.echo(message=e, err=True) + + @deleteObject.command("entities") @click.option("-c", "--collection-name", "collectionName", help="Collection name.") @click.option( diff --git a/milvus_cli/utils.py b/milvus_cli/utils.py index 46f656e..01cb221 100644 --- a/milvus_cli/utils.py +++ b/milvus_cli/utils.py @@ -6,6 +6,8 @@ from Types import DataTypeByNum from Types import ParameterException, ConnectException from time import time +from string import Template +from pymilvus import __version__ def getPackageVersion(): @@ -89,7 +91,8 @@ def _list_field_names(self, collectionName, showVectorOnly=False): result = target.schema.fields if showVectorOnly: return reduce( - lambda x, y: x + [y.name] if y.dtype in [100, 101] else x, result, [] + lambda x, y: x + [y.name] if y.dtype in [100, + 101] else x, result, [] ) return [i.name for i in result] @@ -194,9 +197,11 @@ def releaseCollection(self, collectionName): ) def releasePartition(self, collectionName, partitionName): - targetPartition = self.getTargetPartition(collectionName, partitionName) + targetPartition = self.getTargetPartition( + collectionName, partitionName) targetPartition.release() - result = self.showCollectionLoadingProgress(collectionName, [partitionName]) + result = self.showCollectionLoadingProgress( + collectionName, [partitionName]) return result def releasePartitions(self, collectionName, partitionNameList): @@ -204,16 +209,19 @@ def releasePartitions(self, collectionName, partitionNameList): for name in partitionNameList: tmp = self.releasePartition(collectionName, name) result.append( - [name, tmp.get("num_loaded_entities"), tmp.get("num_total_entities")] + [name, tmp.get("num_loaded_entities"), + tmp.get("num_total_entities")] ) return tabulate( result, headers=["Partition Name", "Loaded", "Total"], tablefmt="grid" ) def loadPartition(self, collectionName, partitionName): - targetPartition = self.getTargetPartition(collectionName, partitionName) + targetPartition = self.getTargetPartition( + collectionName, partitionName) targetPartition.load() - result = self.showCollectionLoadingProgress(collectionName, [partitionName]) + result = self.showCollectionLoadingProgress( + collectionName, [partitionName]) return result def loadPartitions(self, collectionName, partitionNameList): @@ -221,7 +229,8 @@ def loadPartitions(self, collectionName, partitionNameList): for name in partitionNameList: tmp = self.loadPartition(collectionName, name) result.append( - [name, tmp.get("num_loaded_entities"), tmp.get("num_total_entities")] + [name, tmp.get("num_loaded_entities"), + tmp.get("num_total_entities")] ) return tabulate( result, headers=["Partition Name", "Loaded", "Total"], tablefmt="grid" @@ -280,8 +289,10 @@ def getCollectionDetails(self, collectionName="", collection=None): schemaDetails = """Description: {}\n\nAuto ID: {}\n\nFields(* is the primary field):{}""".format( schema.description, schema.auto_id, fieldSchemaDetails ) - partitionDetails = " - " + "\n- ".join(map(lambda x: x.name, partitions)) - indexesDetails = " - " + "\n- ".join(map(lambda x: x.field_name, indexes)) + partitionDetails = " - " + \ + "\n- ".join(map(lambda x: x.name, partitions)) + indexesDetails = " - " + \ + "\n- ".join(map(lambda x: x.field_name, indexes)) rows.append(["Name", target.name]) rows.append(["Description", target.description]) rows.append(["Is Empty", target.is_empty]) @@ -313,7 +324,8 @@ def getIndexDetails(self, collection): rows.append(["Index Type", index.params["index_type"]]) rows.append(["Metric Type", index.params["metric_type"]]) params = index.params["params"] - paramsDetails = "\n- ".join(map(lambda k: f"{k[0]}: {k[1]}", params.items())) + paramsDetails = "\n- ".join( + map(lambda k: f"{k[0]}: {k[1]}", params.items())) rows.append(["Params", paramsDetails]) return tabulate(rows, tablefmt="grid") @@ -329,7 +341,8 @@ def createCollection( if fieldType in ["BINARY_VECTOR", "FLOAT_VECTOR"]: fieldList.append( FieldSchema( - name=fieldName, dtype=DataType[fieldType], dim=int(fieldData) + name=fieldName, dtype=DataType[fieldType], dim=int( + fieldData) ) ) else: @@ -433,7 +446,8 @@ def query(self, collectionName, queryParameters): def insert(self, collectionName, data, partitionName=None, timeout=None): collection = self.getTargetCollection(collectionName) - result = collection.insert(data, partition_name=partitionName, timeout=timeout) + result = collection.insert( + data, partition_name=partitionName, timeout=timeout) entitiesNum = collection.num_entities return [result, entitiesNum] @@ -459,7 +473,8 @@ def calcDistance(self, vectors_left, vectors_right, params=None, timeout=None): def deleteEntities(self, expr, collectionName, partition_name=None, timeout=None): collection = self.getTargetCollection(collectionName) - result = collection.delete(expr, partition_name=partition_name, timeout=timeout) + result = collection.delete( + expr, partition_name=partition_name, timeout=timeout) return result def getQuerySegmentInfo(self, collectionName, timeout=None, prettierFormat=False): @@ -471,7 +486,8 @@ def getQuerySegmentInfo(self, collectionName, timeout=None, prettierFormat=False if not prettierFormat or not result: return result firstChild = result[0] - headers = ["segmentID", "collectionID", "partitionID", "mem_size", "num_rows"] + headers = ["segmentID", "collectionID", + "partitionID", "mem_size", "num_rows"] return tabulate( [[getattr(_, i) for i in headers] for _ in result], headers=headers, @@ -579,6 +595,21 @@ def getCompactCollectionPlans(self, collectionName, timeout=None): collection = self.getTargetCollection(collectionName) return collection.get_compaction_plans(timeout=timeout) + def listCredUsers(self): + from pymilvus import list_cred_users + users = list_cred_users(self.alias) + return users + + def createCredUser(self, username=None, password=None): + from pymilvus import create_credential + create_credential(username, password, self.alias) + return self.listCredUsers() + + def deleteCredUser(self, username=None): + from pymilvus import delete_credential + delete_credential(username, self.alias) + return self.listCredUsers() + class Completer(object): # COMMANDS = ['clear', 'connect', 'create', 'delete', 'describe', 'exit', @@ -594,13 +625,13 @@ class Completer(object): "clear": [], "compact": [], "connect": [], - "create": ["alias", "collection", "partition", "index"], - "delete": ["alias", "collection", "entities", "partition", "index"], + "create": ["alias", "collection", "partition", "index","user"], + "delete": ["alias", "collection", "entities", "partition", "index","user"], "describe": ["collection", "partition", "index"], "exit": [], "help": [], "import": [], - "list": ["collections", "partitions", "indexes"], + "list": ["collections", "partitions", "indexes","users"], "load_balance": [], "load": [], "query": [], @@ -702,11 +733,12 @@ def complete(self, text, state): if args: return (impl(args) + [None])[state] return [cmd + " "][state] - results = [c + " " for c in self.COMMANDS if c.startswith(cmd)] + [None] + results = [ + c + " " for c in self.COMMANDS if c.startswith(cmd)] + [None] return results[state] -WELCOME_MSG = """ +msgTemp = Template(""" __ __ _ _ ____ _ ___ @@ -715,9 +747,14 @@ def complete(self, text, state): | | | | | |\ V /| |_| \__ \ | |___| |___ | | |_| |_|_|_| \_/ \__,_|___/ \____|_____|___| - +Milvus cli version: ${cli} +Pymilvus version: ${py} + Learn more: https://github.com/zilliztech/milvus_cli. -""" +""") + + +WELCOME_MSG = msgTemp.safe_substitute(cli=getPackageVersion(), py=__version__) EXIT_MSG = "\n\nThanks for using.\nWe hope your feedback: https://github.com/zilliztech/milvus_cli/issues/new.\n\n" From 5bed9a7df0a37394cbf533044e4f3e633ac8cd7e Mon Sep 17 00:00:00 2001 From: nameczz Date: Fri, 29 Jul 2022 09:39:11 +0800 Subject: [PATCH 2/4] add ignore Signed-off-by: nameczz --- .gitignore | 1 + Dockerfile.test | 1 + setup.py | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Dockerfile.test diff --git a/.gitignore b/.gitignore index b6e4761..0af027c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.py[cod] *$py.class +.vscode # C extensions *.so diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..5961d3c --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1 @@ +From python:3.9-slim diff --git a/setup.py b/setup.py index 2559b61..a8c7269 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="milvus_cli", - version="v0.2.0", + version="v0.3.0", author="Milvus Team", author_email="milvus-team@zilliz.com", url="https://github.com/zilliztech/milvus_cli", @@ -17,7 +17,7 @@ include_package_data=True, install_requires=[ "Click==8.0.1", - "pymilvus==2.0.0", + "pymilvus==2.1.0", "tabulate==0.8.9", "requests==2.26.0", ], From f0e133414abd6ced9efde78395a7833510d254d2 Mon Sep 17 00:00:00 2001 From: nameczz Date: Fri, 29 Jul 2022 14:13:57 +0800 Subject: [PATCH 3/4] support connect with auth Signed-off-by: nameczz --- milvus_cli/scripts/milvus_cli.py | 28 ++++++++++++++++++++++++++-- milvus_cli/utils.py | 7 +++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/milvus_cli/scripts/milvus_cli.py b/milvus_cli/scripts/milvus_cli.py index a7d533e..7ba1166 100644 --- a/milvus_cli/scripts/milvus_cli.py +++ b/milvus_cli/scripts/milvus_cli.py @@ -66,6 +66,30 @@ def help(): default=19530, type=int, ) +@click.option( + "-s", + "--secure", + "secure", + help="[Optional] - Secure, default is `False`.", + default=False, + type=bool, +) +@click.option( + "-u", + "--username", + "username", + help="[Optional] - Username , default is `None`.", + default=None, + type=str, +) +@click.option( + "-pwd", + "--password", + "password", + help="[Optional] - Password , default is `None`.", + default=None, + type=str, +) @click.option( "-D", "--disconnect", @@ -75,7 +99,7 @@ def help(): is_flag=True, ) @click.pass_obj -def connect(obj, alias, host, port, disconnect): +def connect(obj, alias, host, port,secure,username,password, disconnect): """ Connect to Milvus. @@ -84,7 +108,7 @@ def connect(obj, alias, host, port, disconnect): milvus_cli > connect -h 127.0.0.1 -p 19530 -a default """ try: - obj.connect(alias, host, port, disconnect) + obj.connect(alias, host, port, disconnect,secure,username,password) except Exception as e: click.echo(message=e, err=True) else: diff --git a/milvus_cli/utils.py b/milvus_cli/utils.py index 01cb221..641afb9 100644 --- a/milvus_cli/utils.py +++ b/milvus_cli/utils.py @@ -37,16 +37,19 @@ class PyOrm(object): port = 19530 alias = "default" - def connect(self, alias=None, host=None, port=None, disconnect=False): + def connect(self, alias=None, host=None, port=None, disconnect=False, secure=False, username=None, password=None): self.alias = alias self.host = host self.port = port + trimUsername = None if username is None else username.strip() + trimPwd = None if password is None else password.strip() + from pymilvus import connections if disconnect: connections.disconnect(alias) return - connections.connect(self.alias, host=self.host, port=self.port) + connections.connect(self.alias, host=self.host, port=self.port,user=trimUsername,password=trimPwd,secure=secure) def checkConnection(self): from pymilvus import list_collections From a5ee6cdbc5512f3a24503ee3c73fc54a1fe02647 Mon Sep 17 00:00:00 2001 From: nameczz Date: Fri, 29 Jul 2022 16:54:45 +0800 Subject: [PATCH 4/4] fix pymilvus 2.1 change Signed-off-by: nameczz --- milvus_cli/utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/milvus_cli/utils.py b/milvus_cli/utils.py index 641afb9..d2db916 100644 --- a/milvus_cli/utils.py +++ b/milvus_cli/utils.py @@ -70,10 +70,10 @@ def showConnection(self, alias="default", showAll=False): ) aliasList = map(lambda x: x[0], allConnections) if tempAlias in aliasList: - host, port = connections.get_connection_addr(tempAlias).values() + secure, host, port = connections.get_connection_addr(tempAlias).values() # return """Host: {}\nPort: {}\nAlias: {}""".format(host, port, alias) return tabulate( - [["Host", host], ["Port", port], ["Alias", tempAlias]], + [["Host", host], ["Port", port], ["Alias", tempAlias],["Secure",secure]], tablefmt="pretty", ) else: @@ -121,7 +121,7 @@ def listCollections(self, timeout=None, showLoadedOnly=False): collectionNames = self._list_collection_names(timeout) for name in collectionNames: loadingProgress = self.showCollectionLoadingProgress(name) - loaded, total = loadingProgress.values() + loaded, total, alias = loadingProgress.values() # isLoaded = (total > 0) and (loaded == total) # shouldBeAdded = isLoaded if showLoadedOnly else True # if shouldBeAdded: @@ -433,7 +433,6 @@ def search(self, collectionName, searchParameters, prettierFormat=True): def query(self, collectionName, queryParameters): collection = self.getTargetCollection(collectionName) - collection.load() print(queryParameters) res = collection.query(**queryParameters) # return f"- Query results: {res}"