Add VM list filtering by tag

Uses same method as state or node filtering, rather than altering how
the main LIMIT field works.
This commit is contained in:
Joshua Boniface 2021-07-14 00:51:48 -04:00
parent 9ea9ac3b8a
commit 75fb60b1b4
8 changed files with 107 additions and 32 deletions

View File

@ -592,7 +592,7 @@ class API_Node_Root(Resource):
name: limit
type: string
required: false
description: A search limit; fuzzy by default, use ^/$ to force exact matches
description: A search limit in the name, tags, or an exact UUID; fuzzy by default, use ^/$ to force exact matches
- in: query
name: daemon_state
type: string
@ -844,6 +844,7 @@ class API_VM_Root(Resource):
{'name': 'limit'},
{'name': 'node'},
{'name': 'state'},
{'name': 'tag'},
])
@Authenticator
def get(self, reqargs):
@ -1092,7 +1093,7 @@ class API_VM_Root(Resource):
name: limit
type: string
required: false
description: A name search limit; fuzzy by default, use ^/$ to force exact matches
description: A search limit in the name, tags, or an exact UUID; fuzzy by default, use ^/$ to force exact matches
- in: query
name: node
type: string
@ -1103,6 +1104,11 @@ class API_VM_Root(Resource):
type: string
required: false
description: Limit list to VMs in this state
- in: query
name: tag
type: string
required: false
description: Limit list to VMs with this tag
responses:
200:
description: OK
@ -1114,6 +1120,7 @@ class API_VM_Root(Resource):
return api_helper.vm_list(
reqargs.get('node', None),
reqargs.get('state', None),
reqargs.get('tag', None),
reqargs.get('limit', None)
)
@ -1244,7 +1251,7 @@ class API_VM_Element(Resource):
type: object
id: Message
"""
return api_helper.vm_list(None, None, vm, is_fuzzy=False)
return api_helper.vm_list(None, None, None, vm, is_fuzzy=False)
@RequestParser([
{'name': 'limit'},

View File

@ -326,7 +326,7 @@ def vm_state(zkhandler, vm):
"""
Return the state of virtual machine VM.
"""
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, vm, is_fuzzy=False)
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False)
if retflag:
if retdata:
@ -355,7 +355,7 @@ def vm_node(zkhandler, vm):
"""
Return the current node of virtual machine VM.
"""
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, vm, is_fuzzy=False)
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False)
if retflag:
if retdata:
@ -409,11 +409,11 @@ def vm_console(zkhandler, vm, lines=None):
@pvc_common.Profiler(config)
@ZKConnection(config)
def vm_list(zkhandler, node=None, state=None, limit=None, is_fuzzy=True):
def vm_list(zkhandler, node=None, state=None, tag=None, limit=None, is_fuzzy=True):
"""
Return a list of VMs with limit LIMIT.
"""
retflag, retdata = pvc_vm.get_list(zkhandler, node, state, limit, is_fuzzy)
retflag, retdata = pvc_vm.get_list(zkhandler, node, state, tag, limit, is_fuzzy)
if retflag:
if retdata:
@ -800,7 +800,7 @@ def vm_flush_locks(zkhandler, vm):
"""
Flush locks of a (stopped) VM.
"""
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, vm, is_fuzzy=False)
retflag, retdata = pvc_vm.get_list(zkhandler, None, None, None, vm, is_fuzzy=False)
if retdata[0].get('state') not in ['stop', 'disable']:
return {"message": "VM must be stopped to flush locks"}, 400

View File

@ -54,12 +54,12 @@ def vm_info(config, vm):
return False, response.json().get('message', '')
def vm_list(config, limit, target_node, target_state):
def vm_list(config, limit, target_node, target_state, target_tag):
"""
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}
API arguments: limit={limit}, node={target_node}, state={target_state}, tag={target_tag}
API schema: [{json_data_object},{json_data_object},etc.]
"""
params = dict()
@ -69,6 +69,8 @@ def vm_list(config, limit, target_node, target_state):
params['node'] = target_node
if target_state:
params['state'] = target_state
if target_tag:
params['tag'] = target_tag
response = call_api(config, 'get', '/vm', params=params)

View File

@ -1747,19 +1747,23 @@ def vm_dump(filename, domain):
'-s', '--state', 'target_state', default=None,
help='Limit list to VMs in the specified state.'
)
@click.option(
'-g', '--tag', 'target_tag', default=None,
help='Limit list to VMs with the specified tag.'
)
@click.option(
'-r', '--raw', 'raw', is_flag=True, default=False,
help='Display the raw list of VM names only.'
)
@cluster_req
def vm_list(target_node, target_state, limit, raw):
def vm_list(target_node, target_state, target_tag, limit, raw):
"""
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)
retcode, retdata = pvc_vm.vm_list(config, limit, target_node, target_state, target_tag)
if retcode:
retdata = pvc_vm.format_list(config, retdata, raw)
else:

View File

@ -60,7 +60,7 @@ def getClusterInformation(zkhandler):
retcode, node_list = pvc_node.get_list(zkhandler, None)
# Get vm information object list
retcode, vm_list = pvc_vm.get_list(zkhandler, None, None, None)
retcode, vm_list = pvc_vm.get_list(zkhandler, None, None, None, None)
# Get network information object list
retcode, network_list = pvc_network.get_list(zkhandler, None, None)

View File

@ -866,7 +866,7 @@ def get_info(zkhandler, domain):
return True, domain_information
def get_list(zkhandler, node, state, limit, is_fuzzy=True):
def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True):
if node:
# Verify node is valid
if not common.verifyNode(zkhandler, node):
@ -904,6 +904,7 @@ def get_list(zkhandler, node, state, limit, is_fuzzy=True):
for vm in full_vm_list:
name = zkhandler.read(('domain', vm))
is_limit_match = False
is_tag_match = False
is_node_match = False
is_state_match = False
@ -920,6 +921,13 @@ def get_list(zkhandler, node, state, limit, is_fuzzy=True):
else:
is_limit_match = True
if tag:
vm_tags = zkhandler.children(('domain.meta.tags', vm))
if tag in vm_tags:
is_tag_match = True
else:
is_tag_match = True
# Check on node
if node:
vm_node = zkhandler.read(('domain.node', vm))
@ -936,7 +944,7 @@ def get_list(zkhandler, node, state, limit, is_fuzzy=True):
else:
is_state_match = True
get_vm_info[vm] = True if is_limit_match and is_node_match and is_state_match else False
get_vm_info[vm] = True if is_limit_match and is_tag_match and is_node_match and is_state_match else False
# Obtain our VM data in a thread pool
# This helps parallelize the numerous Zookeeper calls a bit, within the bounds of the GIL, and

View File

@ -224,7 +224,8 @@
"tags": {
"description": "The tag(s) of the VM",
"items": {
"type": "string"
"id": "VMTag",
"type": "object"
},
"type": "array"
}
@ -1386,6 +1387,28 @@
"description": "The current state of the VM",
"type": "string"
},
"tags": {
"description": "The tag(s) of the VM",
"items": {
"id": "VMTag",
"properties": {
"name": {
"description": "The name of the tag",
"type": "string"
},
"protected": {
"description": "Whether the tag is protected or not",
"type": "boolean"
},
"type": {
"description": "The type of the tag (user, system)",
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"type": {
"description": "The type of the VM",
"type": "string"
@ -2431,7 +2454,7 @@
"description": "",
"parameters": [
{
"description": "A search limit; fuzzy by default, use ^/$ to force exact matches",
"description": "A search limit in the name, tags, or an exact UUID; fuzzy by default, use ^/$ to force exact matches",
"in": "query",
"name": "limit",
"required": false,
@ -5811,7 +5834,7 @@
"description": "",
"parameters": [
{
"description": "A name search limit; fuzzy by default, use ^/$ to force exact matches",
"description": "A search limit in the name, tags, or an exact UUID; fuzzy by default, use ^/$ to force exact matches",
"in": "query",
"name": "limit",
"required": false,
@ -5830,6 +5853,13 @@
"name": "state",
"required": false,
"type": "string"
},
{
"description": "Limit list to VMs with this tag",
"in": "query",
"name": "tag",
"required": false,
"type": "string"
}
],
"responses": {
@ -5907,12 +5937,22 @@
"type": "string"
},
{
"description": "The tag(s) of the VM",
"description": "The user tag(s) of the VM",
"in": "query",
"items": {
"type": "string"
},
"name": "tags",
"name": "user_tags",
"required": false,
"type": "array"
},
{
"description": "The protected user tag(s) of the VM",
"in": "query",
"items": {
"type": "string"
},
"name": "protected_tags",
"required": false,
"type": "array"
}
@ -6055,12 +6095,22 @@
"type": "string"
},
{
"description": "The tag(s) of the VM",
"description": "The user tag(s) of the VM",
"in": "query",
"items": {
"type": "string"
},
"name": "tags",
"name": "user_tags",
"required": false,
"type": "array"
},
{
"description": "The protected user tag(s) of the VM",
"in": "query",
"items": {
"type": "string"
},
"name": "protected_tags",
"required": false,
"type": "array"
}
@ -6481,11 +6531,10 @@
"description": "",
"parameters": [
{
"description": "The action to perform with the tags, either \"add\" to existing, \"remove\" from existing, or \"replace\" all existing",
"description": "The action to perform with the tag",
"enum": [
"add",
"remove",
"replace"
"remove"
],
"in": "query",
"name": "action",
@ -6493,14 +6542,19 @@
"type": "string"
},
{
"description": "The list of text tags to add/remove/replace-with",
"description": "The text value of the tag",
"in": "query",
"items": {
"name": "tag",
"required": true,
"type": "string"
},
"name": "tags",
"required": true,
"type": "array"
{
"default": false,
"description": "Set the protected state of the tag",
"in": "query",
"name": "protected",
"required": false,
"type": "boolean"
}
],
"responses": {

View File

@ -180,7 +180,7 @@ class MetadataAPIInstance(object):
client_macaddr = host_information.get('mac_address', None)
# Find the VM with that MAC address - we can't assume that the hostname is actually right
_discard, vm_list = pvc_vm.get_list(self.zkhandler, None, None, None)
_discard, vm_list = pvc_vm.get_list(self.zkhandler, None, None, None, None)
vm_details = dict()
for vm in vm_list:
try: