diff --git a/api-daemon/pvcapid/flaskapi.py b/api-daemon/pvcapid/flaskapi.py index 44afd2d7..6458a022 100755 --- a/api-daemon/pvcapid/flaskapi.py +++ b/api-daemon/pvcapid/flaskapi.py @@ -4024,12 +4024,79 @@ class API_Provisioner_Template_System_Element(Resource): node_autostart ) + @RequestParser([ + { 'name': 'vcpus' }, + { 'name': 'vram' }, + { 'name': 'serial' }, + { 'name': 'vnc' }, + { 'name': 'vnc_bind' }, + { 'name': 'node_limit' }, + { 'name': 'node_selector' }, + { 'name': 'node_autostart' } + ]) @Authenticator - def put(self, template): + def put(self, template, reqargs): """ - TODO + Modify an existing system template {template} + --- + tags: + - provisioner / template + parameters: + - in: query + name: vcpus + type: integer + description: vCPU count for VM + - in: query + name: vram + type: integer + description: vRAM size in MB for VM + - in: query + name: serial + type: boolean + description: Whether to enable serial console for VM + - in: query + name: vnc + type: boolean + description: Whether to enable VNC console for VM + - in: query + name: vnc_bind + type: string + description: VNC bind address when VNC console is enabled + - in: query + name: node_limit + type: string + description: CSV list of node(s) to limit VM assignment to + - in: query + name: node_selector + type: string + description: Selector to use for VM node assignment on migration/move + - in: query + name: node_autostart + type: boolean + description: Whether to start VM with node ready state (one-time) + responses: + 200: + description: OK + schema: + type: object + id: Message + 400: + description: Bad request + schema: + type: object + id: Message """ - pass + return api_provisioner.modify_template_system( + template, + reqargs.get('vcpus', None), + reqargs.get('vram', None), + reqargs.get('serial', None), + reqargs.get('vnc', None), + reqargs.get('vnc_bind'), + reqargs.get('node_limit', None), + reqargs.get('node_selector', None), + reqargs.get('node_autostart', None) + ) @Authenticator def delete(self, template): diff --git a/api-daemon/pvcapid/provisioner.py b/api-daemon/pvcapid/provisioner.py index b5ab8ca2..9f2e6074 100755 --- a/api-daemon/pvcapid/provisioner.py +++ b/api-daemon/pvcapid/provisioner.py @@ -30,6 +30,8 @@ import time import shlex import subprocess +from distutils.util import strtobool + import daemon_lib.common as pvc_common import daemon_lib.node as pvc_node import daemon_lib.vm as pvc_vm @@ -341,6 +343,84 @@ def create_template_storage_element(name, disk_id, pool, source_volume=None, dis close_database(conn, cur) return retmsg, retcode +# +# Template Modify functions +# +def modify_template_system(name, vcpu_count=None, vram_mb=None, serial=None, vnc=None, vnc_bind=None, node_limit=None, node_selector=None, node_autostart=None): + if list_profile(name, is_fuzzy=False)[-1] != 200: + retmsg = { 'message': 'The system template "{}" does not exist'.format(name) } + retcode = 400 + return retmsg, retcode + + fields = [] + + if vcpu_count is not None: + try: + vcpu_count = int(vcpu_count) + except: + retmsg = { 'message': 'The vcpus value must be an integer' } + retcode = 400 + return retmsg, retcode + fields.append({'field': 'vcpu_count', 'data': vcpu_count}) + + if vram_mb is not None: + try: + vram_mb = int(vram_mb) + except: + retmsg = { 'message': 'The vram value must be an integer' } + retcode = 400 + return retmsg, retcode + fields.append({'field': 'vram_mb', 'data': vram_mb}) + + if serial is not None: + try: + serial = bool(strtobool(serial)) + except: + retmsg = { 'message': 'The serial value must be a boolean' } + retcode = 400 + return retmsg, retcode + fields.append({'field': 'serial', 'data': serial}) + + if vnc is not None: + try: + vnc = bool(strtobool(vnc)) + except: + retmsg = { 'message': 'The vnc value must be a boolean' } + retcode = 400 + return retmsg, retcode + fields.append({'field': 'vnc', 'data': vnc}) + + if vnc_bind is not None: + fields.append({'field': 'vnc_bind', 'data': vnc_bind}) + + if node_limit is not None: + fields.append({'field': 'node_limit', 'data': node_limit}) + + if node_selector is not None: + fields.append({'field': 'node_selector', 'data': node_selector}) + + if node_autostart is not None: + try: + node_autostart = bool(strtobool(node_autostart)) + except: + retmsg = { 'message': 'The node_autostart value must be a boolean' } + retcode = 400 + fields.append({'field': 'node_autostart', 'data': node_autostart}) + + conn, cur = open_database(config) + try: + for field in fields: + query = "UPDATE system_template SET {} = %s WHERE name = %s;".format(field.get('field')) + args = (field.get('data'), name) + cur.execute(query, args) + retmsg = { "message": 'Modified system template "{}"'.format(name) } + retcode = 200 + except Exception as e: + retmsg = { 'message': 'Failed to modify entry "{}": {}'.format(name, e) } + retcode = 400 + close_database(conn, cur) + return retmsg, retcode + # # Template Delete functions # diff --git a/client-cli/cli_lib/provisioner.py b/client-cli/cli_lib/provisioner.py index 68165a2d..7ffa32fb 100644 --- a/client-cli/cli_lib/provisioner.py +++ b/client-cli/cli_lib/provisioner.py @@ -85,7 +85,24 @@ def template_add(config, params, template_type=None): return retvalue, response.json()['message'] -def template_remove(config, name, template_type=None): +def template_modify(config, params, name, template_type): + """ + Modify an existing template of {template_type} with {params} + + API endpoint: PUT /api/v1/provisioner/template/{template_type}/{name} + API_arguments: args + API schema: {message} + """ + response = call_api(config, 'put', '/provisioner/template/{template_type}/{name}'.format(template_type=template_type, name=name), params=params) + + if response.status_code == 200: + retvalue = True + else: + retvalue = False + + return retvalue, response.json()['message'] + +def template_remove(config, name, template_type): """ Remove template {name} of {template_type} diff --git a/client-cli/pvc.py b/client-cli/pvc.py index 4c4c5f32..5398ccfa 100755 --- a/client-cli/pvc.py +++ b/client-cli/pvc.py @@ -2135,6 +2135,68 @@ def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, no retcode, retdata = pvc_provisioner.template_add(config, params, template_type='system') cleanup(retcode, retdata) +############################################################################### +# pvc provisioner template system modify +############################################################################### +@click.command(name='modify', short_help='Modify an existing system template.') +@click.argument( + 'name' +) +@click.option( + '-u', '--vcpus', 'vcpus', + type=int, + help='The number of vCPUs.' +) +@click.option( + '-m', '--vram', 'vram', + type=int, + help='The amount of vRAM (in MB).' +) +@click.option( + '-s', '--serial', 'serial', + is_flag=True, default=None, + help='Enable the virtual serial console.' +) +@click.option( + '-n', '--vnc', 'vnc', + is_flag=True, default=None, + help='Enable the VNC console.' +) +@click.option( + '-b', '--vnc-bind', 'vnc_bind', + help='Bind VNC to this IP address instead of localhost.' +) +@click.option( + '--node-limit', 'node_limit', + help='Limit VM operation to this CSV list of node(s).' +) +@click.option( + '--node-selector', 'node_selector', + type=click.Choice(['mem', 'vcpus', 'vms', 'load'], case_sensitive=False), + help='Use this selector to determine the optimal node during migrations.' +) +@click.option( + '--node-autostart', 'node_autostart', + is_flag=True, default=None, + help='Autostart VM with their parent Node on first/next boot.' +) +def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart): + """ + Add a new system template NAME to the PVC cluster provisioner. + """ + params = dict() + params['vcpus'] = vcpus + params['vram'] = vram + params['serial'] = serial + params['vnc'] = vnc + params['vnc_bind'] = vnc_bind + params['node_limit'] = node_limit + params['node_selector'] = node_selector + params['node_autostart'] = node_autostart + + retcode, retdata = pvc_provisioner.template_modify(config, params, name, template_type='system') + cleanup(retcode, retdata) + ############################################################################### # pvc provisioner template system remove ############################################################################### @@ -3429,6 +3491,7 @@ cli_storage.add_command(ceph_volume) provisioner_template_system.add_command(provisioner_template_system_list) provisioner_template_system.add_command(provisioner_template_system_add) +provisioner_template_system.add_command(provisioner_template_system_modify) provisioner_template_system.add_command(provisioner_template_system_remove) provisioner_template_network.add_command(provisioner_template_network_list) diff --git a/docs/manuals/swagger.json b/docs/manuals/swagger.json index f388080f..45b38dcf 100644 --- a/docs/manuals/swagger.json +++ b/docs/manuals/swagger.json @@ -3790,6 +3790,77 @@ "tags": [ "provisioner / template" ] + }, + "put": { + "description": "", + "parameters": [ + { + "description": "vCPU count for VM", + "in": "query", + "name": "vcpus", + "type": "integer" + }, + { + "description": "vRAM size in MB for VM", + "in": "query", + "name": "vram", + "type": "integer" + }, + { + "description": "Whether to enable serial console for VM", + "in": "query", + "name": "serial", + "type": "boolean" + }, + { + "description": "Whether to enable VNC console for VM", + "in": "query", + "name": "vnc", + "type": "boolean" + }, + { + "description": "VNC bind address when VNC console is enabled", + "in": "query", + "name": "vnc_bind", + "type": "string" + }, + { + "description": "CSV list of node(s) to limit VM assignment to", + "in": "query", + "name": "node_limit", + "type": "string" + }, + { + "description": "Selector to use for VM node assignment on migration/move", + "in": "query", + "name": "node_selector", + "type": "string" + }, + { + "description": "Whether to start VM with node ready state (one-time)", + "in": "query", + "name": "node_autostart", + "type": "boolean" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Message" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/Message" + } + } + }, + "summary": "Modify an existing system template {template}", + "tags": [ + "provisioner / template" + ] } }, "/api/v1/provisioner/userdata": {