From c0f7ba0125405a9debc67b3c1c1c349296ef789b Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 7 Oct 2021 11:13:30 -0400 Subject: [PATCH] Add limit negation to VM list When using the "state", "node", or "tag" arguments to a VM list, add support for a "negate" flag to look for all VMs *not in* the state, node, or tag state. --- api-daemon/pvcapid/flaskapi.py | 17 ++++++++++++----- api-daemon/pvcapid/helper.py | 10 +++++----- client-cli/pvc/cli_lib/vm.py | 5 +++-- client-cli/pvc/pvc.py | 8 ++++++-- daemon-common/vm.py | 14 ++++++++++---- docs/manuals/swagger.json | 7 +++++++ 6 files changed, 43 insertions(+), 18 deletions(-) diff --git a/api-daemon/pvcapid/flaskapi.py b/api-daemon/pvcapid/flaskapi.py index 987d3900..e428d0e7 100755 --- a/api-daemon/pvcapid/flaskapi.py +++ b/api-daemon/pvcapid/flaskapi.py @@ -891,6 +891,7 @@ class API_VM_Root(Resource): {'name': 'node'}, {'name': 'state'}, {'name': 'tag'}, + {'name': 'negate'}, ]) @Authenticator def get(self, reqargs): @@ -1155,6 +1156,11 @@ class API_VM_Root(Resource): type: string required: false description: Limit list to VMs with this tag + - in: query + name: negate + type: boolean + required: false + description: Negate the specified node, state, or tag limit(s) responses: 200: description: OK @@ -1164,10 +1170,11 @@ class API_VM_Root(Resource): $ref: '#/definitions/vm' """ return api_helper.vm_list( - reqargs.get('node', None), - reqargs.get('state', None), - reqargs.get('tag', None), - reqargs.get('limit', None) + node=reqargs.get('node', None), + state=reqargs.get('state', None), + tag=reqargs.get('tag', None), + limit=reqargs.get('limit', None), + negate=bool(strtobool(reqargs.get('negate', 'False'))), ) @RequestParser([ @@ -1297,7 +1304,7 @@ class API_VM_Element(Resource): type: object id: Message """ - return api_helper.vm_list(None, None, None, vm, is_fuzzy=False) + return api_helper.vm_list(node=None, state=None, tag=None, limit=vm, is_fuzzy=False, negate=False) @RequestParser([ {'name': 'limit'}, diff --git a/api-daemon/pvcapid/helper.py b/api-daemon/pvcapid/helper.py index 3a38d6c1..6e6ee970 100755 --- a/api-daemon/pvcapid/helper.py +++ b/api-daemon/pvcapid/helper.py @@ -354,7 +354,7 @@ def vm_state(zkhandler, vm): """ Return the state of virtual machine VM. """ - retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False) + retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False, negate=False) if retflag: if retdata: @@ -383,7 +383,7 @@ def vm_node(zkhandler, vm): """ Return the current node of virtual machine VM. """ - retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False) + retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False, negate=False) if retflag: if retdata: @@ -437,11 +437,11 @@ def vm_console(zkhandler, vm, lines=None): @pvc_common.Profiler(config) @ZKConnection(config) -def vm_list(zkhandler, node=None, state=None, tag=None, limit=None, is_fuzzy=True): +def vm_list(zkhandler, node=None, state=None, tag=None, limit=None, is_fuzzy=True, negate=False): """ Return a list of VMs with limit LIMIT. """ - retflag, retdata = pvc_vm.get_list(zkhandler, node, state, tag, limit, is_fuzzy) + retflag, retdata = pvc_vm.get_list(zkhandler, node, state, tag, limit, is_fuzzy, negate) if retflag: if retdata: @@ -880,7 +880,7 @@ def vm_flush_locks(zkhandler, vm): """ Flush locks of a (stopped) VM. """ - retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False) + retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False, negate=False) if retdata[0].get('state') not in ['stop', 'disable']: return {"message": "VM must be stopped to flush locks"}, 400 diff --git a/client-cli/pvc/cli_lib/vm.py b/client-cli/pvc/cli_lib/vm.py index ebc7be89..bc6edaa0 100644 --- a/client-cli/pvc/cli_lib/vm.py +++ b/client-cli/pvc/cli_lib/vm.py @@ -54,12 +54,12 @@ def vm_info(config, vm): return False, response.json().get('message', '') -def vm_list(config, limit, target_node, target_state, target_tag): +def vm_list(config, limit, target_node, target_state, target_tag, negate): """ Get list information about VMs (limited by {limit}, {target_node}, or {target_state}) API endpoint: GET /api/v1/vm - API arguments: limit={limit}, node={target_node}, state={target_state}, tag={target_tag} + API arguments: limit={limit}, node={target_node}, state={target_state}, tag={target_tag}, negate={negate} API schema: [{json_data_object},{json_data_object},etc.] """ params = dict() @@ -71,6 +71,7 @@ def vm_list(config, limit, target_node, target_state, target_tag): params['state'] = target_state if target_tag: params['tag'] = target_tag + params['negate'] = negate response = call_api(config, 'get', '/vm', params=params) diff --git a/client-cli/pvc/pvc.py b/client-cli/pvc/pvc.py index 23f49069..0047650f 100755 --- a/client-cli/pvc/pvc.py +++ b/client-cli/pvc/pvc.py @@ -1827,15 +1827,19 @@ def vm_dump(filename, domain): '-r', '--raw', 'raw', is_flag=True, default=False, help='Display the raw list of VM names only.' ) +@click.option( + '-n', '--negate', 'negate', is_flag=True, default=False, + help='Negate the specified node, state, or tag limit(s).' +) @cluster_req -def vm_list(target_node, target_state, target_tag, limit, raw): +def vm_list(target_node, target_state, target_tag, limit, raw, negate): """ List all virtual machines; optionally only match names or full UUIDs matching regex LIMIT. NOTE: Red-coloured network lists indicate one or more configured networks are missing/invalid. """ - retcode, retdata = pvc_vm.vm_list(config, limit, target_node, target_state, target_tag) + retcode, retdata = pvc_vm.vm_list(config, limit, target_node, target_state, target_tag, negate) if retcode: retdata = pvc_vm.format_list(config, retdata, raw) else: diff --git a/daemon-common/vm.py b/daemon-common/vm.py index 56ce03dd..84f48f2e 100644 --- a/daemon-common/vm.py +++ b/daemon-common/vm.py @@ -975,7 +975,7 @@ def get_info(zkhandler, domain): return True, domain_information -def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True): +def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True, negate=False): if node: # Verify node is valid if not common.verifyNode(zkhandler, node): @@ -1032,7 +1032,9 @@ def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True): if tag: vm_tags = zkhandler.children(('domain.meta.tags', vm)) - if tag in vm_tags: + if negate and tag not in vm_tags: + is_tag_match = True + if not negate and tag in vm_tags: is_tag_match = True else: is_tag_match = True @@ -1040,7 +1042,9 @@ def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True): # Check on node if node: vm_node = zkhandler.read(('domain.node', vm)) - if vm_node == node: + if negate and vm_node != node: + is_node_match = True + if not negate and vm_node == node: is_node_match = True else: is_node_match = True @@ -1048,7 +1052,9 @@ def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True): # Check on state if state: vm_state = zkhandler.read(('domain.state', vm)) - if vm_state == state: + if negate and vm_state != state: + is_state_match = True + if not negate and vm_state == state: is_state_match = True else: is_state_match = True diff --git a/docs/manuals/swagger.json b/docs/manuals/swagger.json index 88b1a4c3..33792a67 100644 --- a/docs/manuals/swagger.json +++ b/docs/manuals/swagger.json @@ -5971,6 +5971,13 @@ "name": "tag", "required": false, "type": "string" + }, + { + "description": "Negate the specified node, state, or tag limit(s)", + "in": "query", + "name": "negate", + "required": false, + "type": "boolean" } ], "responses": {