Compare commits

..

1 Commits
v0.9.1 ... list

Author SHA1 Message Date
f3261455c4 Ensure args are checked against form body
Required for XML definitions but erroneously removed during file parsing
reworking.
2020-10-27 02:29:56 -04:00
17 changed files with 67 additions and 259 deletions

View File

@ -21,11 +21,6 @@ To get started with PVC, read the [Cluster Architecture document](https://parall
## Changelog ## Changelog
#### v0.9.1
* Added per-VM migration method feature
* Fixed bug with provisioner system template listing
#### v0.9.0 #### v0.9.0
Numerous small improvements and bugfixes. This release is suitable for general use and is pre-release-quality software. Numerous small improvements and bugfixes. This release is suitable for general use and is pre-release-quality software.

View File

@ -1,28 +0,0 @@
"""PVC version 0.9.0
Revision ID: 3efe890e1d87
Revises: 3bc6117ea44d
Create Date: 2020-10-29 11:49:58.756626
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3efe890e1d87'
down_revision = '3bc6117ea44d'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('system_template', sa.Column('migration_method', sa.Text(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('system_template', 'migration_method')
# ### end Alembic commands ###

View File

@ -834,9 +834,6 @@ class API_VM_Root(Resource):
node_autostart: node_autostart:
type: boolean type: boolean
description: Whether to autostart the VM when its node returns to ready domain state description: Whether to autostart the VM when its node returns to ready domain state
migration_method:
type: string
description: The preferred migration method (live, shutdown, none)
description: description:
type: string type: string
description: The description of the VM description: The description of the VM
@ -1039,7 +1036,6 @@ class API_VM_Root(Resource):
{ 'name': 'node' }, { 'name': 'node' },
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" }, { 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
{ 'name': 'autostart' }, { 'name': 'autostart' },
{ 'name': 'migration_method', 'choices': ('live', 'shutdown', 'none'), 'helptext': "A valid migration_method must be specified" },
{ 'name': 'xml', 'required': True, 'helptext': "A Libvirt XML document must be specified" }, { 'name': 'xml', 'required': True, 'helptext': "A Libvirt XML document must be specified" },
]) ])
@Authenticator @Authenticator
@ -1081,16 +1077,6 @@ class API_VM_Root(Resource):
type: boolean type: boolean
required: false required: false
description: Whether to autostart the VM when its node returns to ready domain state description: Whether to autostart the VM when its node returns to ready domain state
- in: query
name: migration_method
type: string
required: false
description: The preferred migration method (live, shutdown, none)
default: none
enum:
- live
- shutdown
- none
responses: responses:
200: 200:
description: OK description: OK
@ -1108,8 +1094,7 @@ class API_VM_Root(Resource):
reqargs.get('node', None), reqargs.get('node', None),
reqargs.get('limit', None), reqargs.get('limit', None),
reqargs.get('selector', 'mem'), reqargs.get('selector', 'mem'),
bool(strtobool(reqargs.get('autostart', 'false'))), bool(strtobool(reqargs.get('autostart', 'false')))
reqargs.get('migration_method', 'none')
) )
api.add_resource(API_VM_Root, '/vm') api.add_resource(API_VM_Root, '/vm')
@ -1140,7 +1125,6 @@ class API_VM_Element(Resource):
{ 'name': 'node' }, { 'name': 'node' },
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" }, { 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
{ 'name': 'autostart' }, { 'name': 'autostart' },
{ 'name': 'migration_method', 'choices': ('live', 'shutdown', 'none'), 'helptext': "A valid migration_method must be specified" },
{ 'name': 'xml', 'required': True, 'helptext': "A Libvirt XML document must be specified" }, { 'name': 'xml', 'required': True, 'helptext': "A Libvirt XML document must be specified" },
]) ])
@Authenticator @Authenticator
@ -1184,16 +1168,6 @@ class API_VM_Element(Resource):
type: boolean type: boolean
required: false required: false
description: Whether to autostart the VM when its node returns to ready domain state description: Whether to autostart the VM when its node returns to ready domain state
- in: query
name: migration_method
type: string
required: false
description: The preferred migration method (live, shutdown, none)
default: none
enum:
- live
- shutdown
- none
responses: responses:
200: 200:
description: OK description: OK
@ -1211,8 +1185,7 @@ class API_VM_Element(Resource):
reqargs.get('node', None), reqargs.get('node', None),
reqargs.get('limit', None), reqargs.get('limit', None),
reqargs.get('selector', 'mem'), reqargs.get('selector', 'mem'),
bool(strtobool(reqargs.get('autostart', 'false'))), bool(strtobool(reqargs.get('autostart', 'false')))
reqargs.get('migration_method', 'none')
) )
@RequestParser([ @RequestParser([
@ -1323,9 +1296,6 @@ class API_VM_Metadata(Resource):
node_autostart: node_autostart:
type: string type: string
description: Whether to autostart the VM when its node returns to ready domain state description: Whether to autostart the VM when its node returns to ready domain state
migration_method:
type: string
description: The preferred migration method (live, shutdown, none)
404: 404:
description: Not found description: Not found
schema: schema:
@ -1339,7 +1309,6 @@ class API_VM_Metadata(Resource):
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" }, { 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
{ 'name': 'autostart' }, { 'name': 'autostart' },
{ 'name': 'profile' }, { 'name': 'profile' },
{ 'name': 'migration_method', 'choices': ('live', 'shutdown', 'none'), 'helptext': "A valid migration_method must be specified" },
]) ])
@Authenticator @Authenticator
def post(self, vm, reqargs): def post(self, vm, reqargs):
@ -1374,16 +1343,6 @@ class API_VM_Metadata(Resource):
type: string type: string
required: false required: false
description: The PVC provisioner profile for the VM description: The PVC provisioner profile for the VM
- in: query
name: migration_method
type: string
required: false
description: The preferred migration method (live, shutdown, none)
default: none
enum:
- live
- shutdown
- none
responses: responses:
200: 200:
description: OK description: OK
@ -1401,8 +1360,7 @@ class API_VM_Metadata(Resource):
reqargs.get('limit', None), reqargs.get('limit', None),
reqargs.get('selector', None), reqargs.get('selector', None),
reqargs.get('autostart', None), reqargs.get('autostart', None),
reqargs.get('profile', None), reqargs.get('profile', None)
reqargs.get('migration_method', None)
) )
api.add_resource(API_VM_Metadata, '/vm/<vm>/meta') api.add_resource(API_VM_Metadata, '/vm/<vm>/meta')
@ -4099,9 +4057,6 @@ class API_Provisioner_Template_System_Root(Resource):
node_autostart: node_autostart:
type: boolean type: boolean
description: Whether to start VM with node ready state (one-time) description: Whether to start VM with node ready state (one-time)
migration_method:
type: string
description: The preferred migration method (live, shutdown, none)
parameters: parameters:
- in: query - in: query
name: limit name: limit
@ -4129,8 +4084,7 @@ class API_Provisioner_Template_System_Root(Resource):
{ 'name': 'vnc_bind' }, { 'name': 'vnc_bind' },
{ 'name': 'node_limit' }, { 'name': 'node_limit' },
{ 'name': 'node_selector' }, { 'name': 'node_selector' },
{ 'name': 'node_autostart' }, { 'name': 'node_autostart' }
{ 'name': 'migration_method' }
]) ])
@Authenticator @Authenticator
def post(self, reqargs): def post(self, reqargs):
@ -4185,11 +4139,6 @@ class API_Provisioner_Template_System_Root(Resource):
type: boolean type: boolean
required: false required: false
description: Whether to start VM with node ready state (one-time) description: Whether to start VM with node ready state (one-time)
- in: query
name: migration_method
type: string
required: false
description: The preferred migration method (live, shutdown, none)
responses: responses:
200: 200:
description: OK description: OK
@ -4236,8 +4185,7 @@ class API_Provisioner_Template_System_Root(Resource):
vnc_bind, vnc_bind,
reqargs.get('node_limit', None), reqargs.get('node_limit', None),
reqargs.get('node_selector', None), reqargs.get('node_selector', None),
node_autostart, node_autostart
reqargs.get('migration_method', None),
) )
api.add_resource(API_Provisioner_Template_System_Root, '/provisioner/template/system') api.add_resource(API_Provisioner_Template_System_Root, '/provisioner/template/system')
@ -4274,8 +4222,7 @@ class API_Provisioner_Template_System_Element(Resource):
{ 'name': 'vnc_bind' }, { 'name': 'vnc_bind' },
{ 'name': 'node_limit' }, { 'name': 'node_limit' },
{ 'name': 'node_selector' }, { 'name': 'node_selector' },
{ 'name': 'node_autostart' }, { 'name': 'node_autostart' }
{ 'name': 'migration_method' }
]) ])
@Authenticator @Authenticator
def post(self, template, reqargs): def post(self, template, reqargs):
@ -4325,11 +4272,6 @@ class API_Provisioner_Template_System_Element(Resource):
type: boolean type: boolean
required: false required: false
description: Whether to start VM with node ready state (one-time) description: Whether to start VM with node ready state (one-time)
- in: query
name: migration_method
type: string
required: false
description: The preferred migration method (live, shutdown, none)
responses: responses:
200: 200:
description: OK description: OK
@ -4376,8 +4318,7 @@ class API_Provisioner_Template_System_Element(Resource):
vnc_bind, vnc_bind,
reqargs.get('node_limit', None), reqargs.get('node_limit', None),
reqargs.get('node_selector', None), reqargs.get('node_selector', None),
node_autostart, node_autostart
reqargs.get('migration_method', None),
) )
@RequestParser([ @RequestParser([
@ -4388,8 +4329,7 @@ class API_Provisioner_Template_System_Element(Resource):
{ 'name': 'vnc_bind' }, { 'name': 'vnc_bind' },
{ 'name': 'node_limit' }, { 'name': 'node_limit' },
{ 'name': 'node_selector' }, { 'name': 'node_selector' },
{ 'name': 'node_autostart' }, { 'name': 'node_autostart' }
{ 'name': 'migration_method' }
]) ])
@Authenticator @Authenticator
def put(self, template, reqargs): def put(self, template, reqargs):
@ -4431,10 +4371,6 @@ class API_Provisioner_Template_System_Element(Resource):
name: node_autostart name: node_autostart
type: boolean type: boolean
description: Whether to start VM with node ready state (one-time) description: Whether to start VM with node ready state (one-time)
- in: query
name: migration_method
type: string
description: The preferred migration method (live, shutdown, none)
responses: responses:
200: 200:
description: OK description: OK
@ -4456,8 +4392,7 @@ class API_Provisioner_Template_System_Element(Resource):
reqargs.get('vnc_bind'), reqargs.get('vnc_bind'),
reqargs.get('node_limit', None), reqargs.get('node_limit', None),
reqargs.get('node_selector', None), reqargs.get('node_selector', None),
reqargs.get('node_autostart', None), reqargs.get('node_autostart', None)
reqargs.get('migration_method', None)
) )
@Authenticator @Authenticator

View File

@ -431,7 +431,7 @@ def vm_list(node=None, state=None, limit=None, is_fuzzy=True):
return retdata, retcode return retdata, retcode
def vm_define(xml, node, limit, selector, autostart, migration_method): def vm_define(xml, node, limit, selector, autostart):
""" """
Define a VM from Libvirt XML in the PVC cluster. Define a VM from Libvirt XML in the PVC cluster.
""" """
@ -443,7 +443,7 @@ def vm_define(xml, node, limit, selector, autostart, migration_method):
return { 'message': 'XML is malformed or incorrect: {}'.format(e) }, 400 return { 'message': 'XML is malformed or incorrect: {}'.format(e) }, 400
zk_conn = pvc_common.startZKConnection(config['coordinators']) zk_conn = pvc_common.startZKConnection(config['coordinators'])
retflag, retdata = pvc_vm.define_vm(zk_conn, new_cfg, node, limit, selector, autostart, migration_method, profile=None) retflag, retdata = pvc_vm.define_vm(zk_conn, new_cfg, node, limit, selector, autostart, profile=None)
pvc_common.stopZKConnection(zk_conn) pvc_common.stopZKConnection(zk_conn)
if retflag: if retflag:
@ -475,8 +475,7 @@ def get_vm_meta(vm):
'name': vm, 'name': vm,
'node_limit': retdata['node_limit'], 'node_limit': retdata['node_limit'],
'node_selector': retdata['node_selector'], 'node_selector': retdata['node_selector'],
'node_autostart': retdata['node_autostart'], 'node_autostart': retdata['node_autostart']
'migration_method': retdata['migration_method']
} }
else: else:
retcode = 404 retcode = 404
@ -491,7 +490,7 @@ def get_vm_meta(vm):
return retdata, retcode return retdata, retcode
def update_vm_meta(vm, limit, selector, autostart, provisioner_profile, migration_method): def update_vm_meta(vm, limit, selector, autostart, provisioner_profile):
""" """
Update metadata of a VM. Update metadata of a VM.
""" """
@ -501,7 +500,7 @@ def update_vm_meta(vm, limit, selector, autostart, provisioner_profile, migratio
autostart = bool(strtobool(autostart)) autostart = bool(strtobool(autostart))
except: except:
autostart = False autostart = False
retflag, retdata = pvc_vm.modify_vm_metadata(zk_conn, vm, limit, selector, autostart, provisioner_profile, migration_method) retflag, retdata = pvc_vm.modify_vm_metadata(zk_conn, vm, limit, selector, autostart, provisioner_profile)
pvc_common.stopZKConnection(zk_conn) pvc_common.stopZKConnection(zk_conn)
if retflag: if retflag:

View File

@ -35,10 +35,9 @@ class DBSystemTemplate(db.Model):
node_limit = db.Column(db.Text) node_limit = db.Column(db.Text)
node_selector = db.Column(db.Text) node_selector = db.Column(db.Text)
node_autostart = db.Column(db.Boolean, nullable=False) node_autostart = db.Column(db.Boolean, nullable=False)
migration_method = db.Column(db.Text)
ova = db.Column(db.Integer, db.ForeignKey("ova.id"), nullable=True) ova = db.Column(db.Integer, db.ForeignKey("ova.id"), nullable=True)
def __init__(self, name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova=None): def __init__(self, name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, ova=None):
self.name = name self.name = name
self.vcpu_count = vcpu_count self.vcpu_count = vcpu_count
self.vram_mb = vram_mb self.vram_mb = vram_mb
@ -48,7 +47,6 @@ class DBSystemTemplate(db.Model):
self.node_limit = node_limit self.node_limit = node_limit
self.node_selector = node_selector self.node_selector = node_selector
self.node_autostart = node_autostart self.node_autostart = node_autostart
self.migration_method = migration_method
self.ova = ova self.ova = ova
def __repr__(self): def __repr__(self):

View File

@ -214,14 +214,14 @@ def template_list(limit):
# #
# Template Create functions # Template Create functions
# #
def create_template_system(name, vcpu_count, vram_mb, serial=False, vnc=False, vnc_bind=None, node_limit=None, node_selector=None, node_autostart=False, migration_method=None, ova=None): def create_template_system(name, vcpu_count, vram_mb, serial=False, vnc=False, vnc_bind=None, node_limit=None, node_selector=None, node_autostart=False, ova=None):
if list_template_system(name, is_fuzzy=False)[-1] != 404: if list_template_system(name, is_fuzzy=False)[-1] != 404:
retmsg = { 'message': 'The system template "{}" already exists.'.format(name) } retmsg = { 'message': 'The system template "{}" already exists.'.format(name) }
retcode = 400 retcode = 400
return retmsg, retcode return retmsg, retcode
query = "INSERT INTO system_template (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);" query = "INSERT INTO system_template (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, ova) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);"
args = (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova) args = (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, ova)
conn, cur = open_database(config) conn, cur = open_database(config)
try: try:
@ -359,10 +359,10 @@ def create_template_storage_element(name, disk_id, pool, source_volume=None, dis
# #
# Template Modify functions # 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, migration_method=None): 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_template_system(name, is_fuzzy=False)[-1] != 200: if list_profile(name, is_fuzzy=False)[-1] != 200:
retmsg = { 'message': 'The system template "{}" does not exist.'.format(name) } retmsg = { 'message': 'The system template "{}" does not exist.'.format(name) }
retcode = 404 retcode = 400
return retmsg, retcode return retmsg, retcode
fields = [] fields = []
@ -420,9 +420,6 @@ def modify_template_system(name, vcpu_count=None, vram_mb=None, serial=None, vnc
retcode = 400 retcode = 400
fields.append({'field': 'node_autostart', 'data': node_autostart}) fields.append({'field': 'node_autostart', 'data': node_autostart})
if migration_method is not None:
fields.append({'field': 'migration_method', 'data': migration_method})
conn, cur = open_database(config) conn, cur = open_database(config)
try: try:
for field in fields: for field in fields:
@ -1406,8 +1403,7 @@ def create_vm(self, vm_name, vm_profile, define_vm=True, start_vm=True, script_r
node_limit = node_limit.split(',') node_limit = node_limit.split(',')
node_selector = vm_data['system_details']['node_selector'] node_selector = vm_data['system_details']['node_selector']
node_autostart = vm_data['system_details']['node_autostart'] node_autostart = vm_data['system_details']['node_autostart']
migration_method = vm_data['system_details']['migration_method'] retcode, retmsg = pvc_vm.define_vm(zk_conn, vm_schema.strip(), target_node, node_limit, node_selector, node_autostart, vm_profile, initial_state='provision')
retcode, retmsg = pvc_vm.define_vm(zk_conn, vm_schema.strip(), target_node, node_limit, node_selector, node_autostart, migration_method, vm_profile, initial_state='provision')
print(retmsg) print(retmsg)
else: else:
print("Skipping VM definition") print("Skipping VM definition")

View File

@ -717,10 +717,9 @@ def format_list_template_system(template_data):
template_serial_length = 7 template_serial_length = 7
template_vnc_length = 4 template_vnc_length = 4
template_vnc_bind_length = 10 template_vnc_bind_length = 10
template_node_limit_length = 6 template_node_limit_length = 9
template_node_selector_length = 9 template_node_selector_length = 11
template_node_autostart_length = 10 template_node_autostart_length = 11
template_migration_method_length = 10
for template in template_data: for template in template_data:
# template_name column # template_name column
@ -763,22 +762,17 @@ def format_list_template_system(template_data):
_template_node_autostart_length = len(str(template['node_autostart'])) + 1 _template_node_autostart_length = len(str(template['node_autostart'])) + 1
if _template_node_autostart_length > template_node_autostart_length: if _template_node_autostart_length > template_node_autostart_length:
template_node_autostart_length = _template_node_autostart_length template_node_autostart_length = _template_node_autostart_length
# template_migration_method column
_template_migration_method_length = len(str(template['migration_method'])) + 1
if _template_migration_method_length > template_migration_method_length:
template_migration_method_length = _template_migration_method_length
# Format the string (header) # Format the string (header)
template_list_output_header = '{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \ template_list_output_header = '{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \
{template_vcpu: <{template_vcpu_length}} \ {template_vcpu: <{template_vcpu_length}} \
{template_vram: <{template_vram_length}} \ {template_vram: <{template_vram_length}} \
Console: {template_serial: <{template_serial_length}} \ Consoles: {template_serial: <{template_serial_length}} \
{template_vnc: <{template_vnc_length}} \ {template_vnc: <{template_vnc_length}} \
{template_vnc_bind: <{template_vnc_bind_length}} \ {template_vnc_bind: <{template_vnc_bind_length}} \
Meta: {template_node_limit: <{template_node_limit_length}} \ Metadata: {template_node_limit: <{template_node_limit_length}} \
{template_node_selector: <{template_node_selector_length}} \ {template_node_selector: <{template_node_selector_length}} \
{template_node_autostart: <{template_node_autostart_length}} \ {template_node_autostart: <{template_node_autostart_length}}{end_bold}'.format(
{template_migration_method: <{template_migration_method_length}}{end_bold}'.format(
template_name_length=template_name_length, template_name_length=template_name_length,
template_id_length=template_id_length, template_id_length=template_id_length,
template_vcpu_length=template_vcpu_length, template_vcpu_length=template_vcpu_length,
@ -789,7 +783,6 @@ Meta: {template_node_limit: <{template_node_limit_length}} \
template_node_limit_length=template_node_limit_length, template_node_limit_length=template_node_limit_length,
template_node_selector_length=template_node_selector_length, template_node_selector_length=template_node_selector_length,
template_node_autostart_length=template_node_autostart_length, template_node_autostart_length=template_node_autostart_length,
template_migration_method_length=template_migration_method_length,
bold=ansiprint.bold(), bold=ansiprint.bold(),
end_bold=ansiprint.end(), end_bold=ansiprint.end(),
template_state_colour='', template_state_colour='',
@ -803,8 +796,7 @@ Meta: {template_node_limit: <{template_node_limit_length}} \
template_vnc_bind='VNC bind', template_vnc_bind='VNC bind',
template_node_limit='Limit', template_node_limit='Limit',
template_node_selector='Selector', template_node_selector='Selector',
template_node_autostart='Autostart', template_node_autostart='Autostart'
template_migration_method='Migration'
) )
# Keep track of nets we found to be valid to cut down on duplicate API hits # Keep track of nets we found to be valid to cut down on duplicate API hits
@ -816,13 +808,12 @@ Meta: {template_node_limit: <{template_node_limit_length}} \
'{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \ '{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \
{template_vcpu: <{template_vcpu_length}} \ {template_vcpu: <{template_vcpu_length}} \
{template_vram: <{template_vram_length}} \ {template_vram: <{template_vram_length}} \
{template_serial: <{template_serial_length}} \ {template_serial: <{template_serial_length}} \
{template_vnc: <{template_vnc_length}} \ {template_vnc: <{template_vnc_length}} \
{template_vnc_bind: <{template_vnc_bind_length}} \ {template_vnc_bind: <{template_vnc_bind_length}} \
{template_node_limit: <{template_node_limit_length}} \ {template_node_limit: <{template_node_limit_length}} \
{template_node_selector: <{template_node_selector_length}} \ {template_node_selector: <{template_node_selector_length}} \
{template_node_autostart: <{template_node_autostart_length}} \ {template_node_autostart: <{template_node_autostart_length}}{end_bold}'.format(
{template_migration_method: <{template_migration_method_length}}{end_bold}'.format(
template_name_length=template_name_length, template_name_length=template_name_length,
template_id_length=template_id_length, template_id_length=template_id_length,
template_vcpu_length=template_vcpu_length, template_vcpu_length=template_vcpu_length,
@ -833,7 +824,6 @@ Meta: {template_node_limit: <{template_node_limit_length}} \
template_node_limit_length=template_node_limit_length, template_node_limit_length=template_node_limit_length,
template_node_selector_length=template_node_selector_length, template_node_selector_length=template_node_selector_length,
template_node_autostart_length=template_node_autostart_length, template_node_autostart_length=template_node_autostart_length,
template_migration_method_length=template_migration_method_length,
bold='', bold='',
end_bold='', end_bold='',
template_name=str(template['name']), template_name=str(template['name']),
@ -845,8 +835,7 @@ Meta: {template_node_limit: <{template_node_limit_length}} \
template_vnc_bind=str(template['vnc_bind']), template_vnc_bind=str(template['vnc_bind']),
template_node_limit=str(template['node_limit']), template_node_limit=str(template['node_limit']),
template_node_selector=str(template['node_selector']), template_node_selector=str(template['node_selector']),
template_node_autostart=str(template['node_autostart']), template_node_autostart=str(template['node_autostart'])
template_migration_method=str(template['migration_method'])
) )
) )

View File

@ -80,20 +80,19 @@ def vm_list(config, limit, target_node, target_state):
else: else:
return False, response.json().get('message', '') return False, response.json().get('message', '')
def vm_define(config, xml, node, node_limit, node_selector, node_autostart, migration_method): def vm_define(config, xml, node, node_limit, node_selector, node_autostart):
""" """
Define a new VM on the cluster Define a new VM on the cluster
API endpoint: POST /vm API endpoint: POST /vm
API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}, migration_method={migration_method} API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}
API schema: {"message":"{data}"} API schema: {"message":"{data}"}
""" """
params = { params = {
'node': node, 'node': node,
'limit': node_limit, 'limit': node_limit,
'selector': node_selector, 'selector': node_selector,
'autostart': node_autostart, 'autostart': node_autostart
'migration_method': migration_method
} }
data = { data = {
'xml': xml 'xml': xml
@ -130,12 +129,12 @@ def vm_modify(config, vm, xml, restart):
return retstatus, response.json().get('message', '') return retstatus, response.json().get('message', '')
def vm_metadata(config, vm, node_limit, node_selector, node_autostart, migration_method, provisioner_profile): def vm_metadata(config, vm, node_limit, node_selector, node_autostart, provisioner_profile):
""" """
Modify PVC metadata of a VM Modify PVC metadata of a VM
API endpoint: GET /vm/{vm}/meta, POST /vm/{vm}/meta API endpoint: GET /vm/{vm}/meta, POST /vm/{vm}/meta
API arguments: limit={node_limit}, selector={node_selector}, autostart={node_autostart}, migration_method={migration_method} profile={provisioner_profile} API arguments: limit={node_limit}, selector={node_selector}, autostart={node_autostart}, profile={provisioner_profile}
API schema: {"message":"{data}"} API schema: {"message":"{data}"}
""" """
params = dict() params = dict()
@ -150,9 +149,6 @@ def vm_metadata(config, vm, node_limit, node_selector, node_autostart, migration
if node_autostart is not None: if node_autostart is not None:
params['autostart'] = node_autostart params['autostart'] = node_autostart
if migration_method is not None:
params['migration_method'] = migration_method
if provisioner_profile is not None: if provisioner_profile is not None:
params['profile'] = provisioner_profile params['profile'] = provisioner_profile
@ -395,30 +391,24 @@ def format_info(config, domain_information, long_output):
ainformation.append('') ainformation.append('')
ainformation.append('{}Failure reason:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['failed_reason'])) ainformation.append('{}Failure reason:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['failed_reason']))
if not domain_information.get('node_selector'): if not domain_information['node_selector']:
formatted_node_selector = "False" formatted_node_selector = "False"
else: else:
formatted_node_selector = domain_information['node_selector'] formatted_node_selector = domain_information['node_selector']
if not domain_information.get('node_limit'): if not domain_information['node_limit']:
formatted_node_limit = "False" formatted_node_limit = "False"
else: else:
formatted_node_limit = ', '.join(domain_information['node_limit']) formatted_node_limit = ', '.join(domain_information['node_limit'])
if not domain_information.get('node_autostart'): if not domain_information['node_autostart']:
formatted_node_autostart = "False" formatted_node_autostart = "False"
else: else:
formatted_node_autostart = domain_information['node_autostart'] formatted_node_autostart = domain_information['node_autostart']
if not domain_information.get('migration_method'):
formatted_migration_method = "none"
else:
formatted_migration_method = domain_information['migration_method']
ainformation.append('{}Migration selector:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_selector)) ainformation.append('{}Migration selector:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_selector))
ainformation.append('{}Node limit:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_limit)) ainformation.append('{}Node limit:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_limit))
ainformation.append('{}Autostart:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_autostart)) ainformation.append('{}Autostart:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_autostart))
ainformation.append('{}Migration Method:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_migration_method))
# Network list # Network list
net_list = [] net_list = []

View File

@ -593,16 +593,11 @@ def cli_vm():
'-a/-A', '--autostart/--no-autostart', 'node_autostart', is_flag=True, default=False, '-a/-A', '--autostart/--no-autostart', 'node_autostart', is_flag=True, default=False,
help='Start VM automatically on next unflush/ready state of home node; unset by daemon once used.' help='Start VM automatically on next unflush/ready state of home node; unset by daemon once used.'
) )
@click.option(
'-m', '--method', 'migration_method', default='none', show_default=True,
type=click.Choice(['none','live','shutdown']),
help='The preferred migration method of the VM between nodes; saved with VM.'
)
@click.argument( @click.argument(
'vmconfig', type=click.File() 'vmconfig', type=click.File()
) )
@cluster_req @cluster_req
def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart, migration_method): def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart):
""" """
Define a new virtual machine from Libvirt XML configuration file VMCONFIG. Define a new virtual machine from Libvirt XML configuration file VMCONFIG.
""" """
@ -618,7 +613,7 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart,
except: except:
cleanup(False, 'Error: XML is malformed or invalid') cleanup(False, 'Error: XML is malformed or invalid')
retcode, retmsg = pvc_vm.vm_define(config, new_cfg, target_node, node_limit, node_selector, node_autostart, migration_method) retcode, retmsg = pvc_vm.vm_define(config, new_cfg, target_node, node_limit, node_selector, node_autostart)
cleanup(retcode, retmsg) cleanup(retcode, retmsg)
############################################################################### ###############################################################################
@ -638,11 +633,6 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart,
'-a/-A', '--autostart/--no-autostart', 'node_autostart', is_flag=True, default=None, '-a/-A', '--autostart/--no-autostart', 'node_autostart', is_flag=True, default=None,
help='Start VM automatically on next unflush/ready state of home node; unset by daemon once used.' help='Start VM automatically on next unflush/ready state of home node; unset by daemon once used.'
) )
@click.option(
'-m', '--method', 'migration_method', default='none', show_default=True,
type=click.Choice(['none','live','shutdown']),
help='The preferred migration method of the VM between nodes; saved with VM.'
)
@click.option( @click.option(
'-p', '--profile', 'provisioner_profile', default=None, show_default=False, '-p', '--profile', 'provisioner_profile', default=None, show_default=False,
help='PVC provisioner profile name for VM.' help='PVC provisioner profile name for VM.'
@ -651,15 +641,15 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart,
'domain' 'domain'
) )
@cluster_req @cluster_req
def vm_meta(domain, node_limit, node_selector, node_autostart, migration_method, provisioner_profile): def vm_meta(domain, node_limit, node_selector, node_autostart, provisioner_profile):
""" """
Modify the PVC metadata of existing virtual machine DOMAIN. At least one option to update must be specified. DOMAIN may be a UUID or name. Modify the PVC metadata of existing virtual machine DOMAIN. At least one option to update must be specified. DOMAIN may be a UUID or name.
""" """
if node_limit is None and node_selector is None and node_autostart is None and migration_method is None and provisioner_profile is None: if node_limit is None and node_selector is None and node_autostart is None and provisioner_profile is None:
cleanup(False, 'At least one metadata option must be specified to update.') cleanup(False, 'At least one metadata option must be specified to update.')
retcode, retmsg = pvc_vm.vm_metadata(config, domain, node_limit, node_selector, node_autostart, migration_method, provisioner_profile) retcode, retmsg = pvc_vm.vm_metadata(config, domain, node_limit, node_selector, node_autostart, provisioner_profile)
cleanup(retcode, retmsg) cleanup(retcode, retmsg)
############################################################################### ###############################################################################
@ -2311,14 +2301,8 @@ def provisioner_template_system_list(limit):
is_flag=True, default=False, is_flag=True, default=False,
help='Autostart VM with their parent Node on first/next boot.' help='Autostart VM with their parent Node on first/next boot.'
) )
@click.option(
'--migration-method', 'migration_method',
type=click.Choice(['none','live','shutdown'], case_sensitive=False),
default=None, # Use cluster default
help='The preferred migration method of the VM between nodes'
)
@cluster_req @cluster_req
def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method): def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart):
""" """
Add a new system template NAME to the PVC cluster provisioner. Add a new system template NAME to the PVC cluster provisioner.
""" """
@ -2336,8 +2320,6 @@ def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, no
params['node_selector'] = node_selector params['node_selector'] = node_selector
if node_autostart: if node_autostart:
params['node_autostart'] = node_autostart params['node_autostart'] = node_autostart
if migration_method:
params['migration_method'] = migration_method
retcode, retdata = pvc_provisioner.template_add(config, params, template_type='system') retcode, retdata = pvc_provisioner.template_add(config, params, template_type='system')
cleanup(retcode, retdata) cleanup(retcode, retdata)
@ -2387,14 +2369,8 @@ def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, no
is_flag=True, default=None, is_flag=True, default=None,
help='Autostart VM with their parent Node on first/next boot.' help='Autostart VM with their parent Node on first/next boot.'
) )
@click.option(
'--migration-method', 'migration_method',
type=click.Choice(['none','live','shutdown'], case_sensitive=False),
default=None, # Use cluster default
help='The preferred migration method of the VM between nodes'
)
@cluster_req @cluster_req
def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method): 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. Add a new system template NAME to the PVC cluster provisioner.
""" """
@ -2407,7 +2383,6 @@ def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind,
params['node_limit'] = node_limit params['node_limit'] = node_limit
params['node_selector'] = node_selector params['node_selector'] = node_selector
params['node_autostart'] = node_autostart params['node_autostart'] = node_autostart
params['migration_method'] = migration_method
retcode, retdata = pvc_provisioner.template_modify(config, params, name, template_type='system') retcode, retdata = pvc_provisioner.template_modify(config, params, name, template_type='system')
cleanup(retcode, retdata) cleanup(retcode, retdata)

View File

@ -236,10 +236,6 @@ def getInformationFromXML(zk_conn, uuid):
domain_node_autostart = zkhandler.readdata(zk_conn, '/domains/{}/node_autostart'.format(uuid)) domain_node_autostart = zkhandler.readdata(zk_conn, '/domains/{}/node_autostart'.format(uuid))
except: except:
domain_node_autostart = None domain_node_autostart = None
try:
domain_migration_method = zkhandler.readdata(zk_conn, '/domains/{}/migration_method'.format(uuid))
except:
domain_migration_method = None
if not domain_node_limit: if not domain_node_limit:
domain_node_limit = None domain_node_limit = None
@ -286,7 +282,6 @@ def getInformationFromXML(zk_conn, uuid):
'node_limit': domain_node_limit, 'node_limit': domain_node_limit,
'node_selector': domain_node_selector, 'node_selector': domain_node_selector,
'node_autostart': bool(strtobool(domain_node_autostart)), 'node_autostart': bool(strtobool(domain_node_autostart)),
'migration_method': domain_migration_method,
'description': domain_description, 'description': domain_description,
'profile': domain_profile, 'profile': domain_profile,
'memory': int(domain_memory), 'memory': int(domain_memory),

View File

@ -157,7 +157,7 @@ def flush_locks(zk_conn, domain):
return success, message return success, message
def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart, migration_method=None, profile=None, initial_state='stop'): def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart, profile=None, initial_state='stop'):
# Parse the XML data # Parse the XML data
try: try:
parsed_xml = lxml.objectify.fromstring(config_data) parsed_xml = lxml.objectify.fromstring(config_data)
@ -206,7 +206,6 @@ def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node
'/domains/{}/node_limit'.format(dom_uuid): formatted_node_limit, '/domains/{}/node_limit'.format(dom_uuid): formatted_node_limit,
'/domains/{}/node_selector'.format(dom_uuid): node_selector, '/domains/{}/node_selector'.format(dom_uuid): node_selector,
'/domains/{}/node_autostart'.format(dom_uuid): node_autostart, '/domains/{}/node_autostart'.format(dom_uuid): node_autostart,
'/domains/{}/migration_method'.format(dom_uuid): migration_method,
'/domains/{}/failedreason'.format(dom_uuid): '', '/domains/{}/failedreason'.format(dom_uuid): '',
'/domains/{}/consolelog'.format(dom_uuid): '', '/domains/{}/consolelog'.format(dom_uuid): '',
'/domains/{}/rbdlist'.format(dom_uuid): formatted_rbd_list, '/domains/{}/rbdlist'.format(dom_uuid): formatted_rbd_list,
@ -216,7 +215,7 @@ def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node
return True, 'Added new VM with Name "{}" and UUID "{}" to database.'.format(dom_name, dom_uuid) return True, 'Added new VM with Name "{}" and UUID "{}" to database.'.format(dom_name, dom_uuid)
def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostart, provisioner_profile, migration_method): def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostart, provisioner_profile):
dom_uuid = getDomainUUID(zk_conn, domain) dom_uuid = getDomainUUID(zk_conn, domain)
if not dom_uuid: if not dom_uuid:
return False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain) return False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain)
@ -241,11 +240,6 @@ def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostar
'/domains/{}/profile'.format(dom_uuid): provisioner_profile '/domains/{}/profile'.format(dom_uuid): provisioner_profile
}) })
if migration_method is not None:
zkhandler.writedata(zk_conn, {
'/domains/{}/migration_method'.format(dom_uuid): migration_method
})
return True, 'Successfully modified PVC metadata of VM "{}".'.format(domain) return True, 'Successfully modified PVC metadata of VM "{}".'.format(domain)
def modify_vm(zk_conn, domain, restart, new_vm_config): def modify_vm(zk_conn, domain, restart, new_vm_config):
@ -787,15 +781,9 @@ def format_info(zk_conn, domain_information, long_output):
else: else:
formatted_node_autostart = domain_information['node_autostart'] formatted_node_autostart = domain_information['node_autostart']
if not domain_information['migration_method']:
formatted_migration_method = "False"
else:
formatted_migration_method = domain_information['migration_method']
ainformation.append('{}Migration selector:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_selector)) ainformation.append('{}Migration selector:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_selector))
ainformation.append('{}Node limit:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_limit)) ainformation.append('{}Node limit:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_limit))
ainformation.append('{}Autostart:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_autostart)) ainformation.append('{}Autostart:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_autostart))
ainformation.append('{}Migration Method:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_migration_method))
# Network list # Network list
net_list = [] net_list = []

7
debian/changelog vendored
View File

@ -1,10 +1,3 @@
pvc (0.9.1-0) unstable; urgency=high
* Added per-VM migration method feature
* Fixed bug with provisioner system template listing
-- Joshua Boniface <joshua@boniface.me> Thu, 29 Oct 2020 12:15:28 -0400
pvc (0.9.0-0) unstable; urgency=high pvc (0.9.0-0) unstable; urgency=high
* Numerous bugfixes and improvements * Numerous bugfixes and improvements

2
debian/control vendored
View File

@ -8,7 +8,7 @@ X-Python3-Version: >= 3.2
Package: pvc-daemon-node Package: pvc-daemon-node
Architecture: all Architecture: all
Depends: systemd, pvc-daemon-common, python3-kazoo, python3-psutil, python3-apscheduler, python3-libvirt, python3-psycopg2, python3-dnspython, python3-yaml, python3-distutils, python3-rados, python3-gevent, ipmitool, libvirt-daemon-system, arping, vlan, bridge-utils, dnsmasq, nftables, pdns-server, pdns-backend-pgsql Depends: systemd, pvc-daemon-common, python3-kazoo, python3-psutil, python3-apscheduler, python3-libvirt, python3-psycopg2, python3-dnspython, python3-yaml, python3-distutils, python3-rados, ipmitool, libvirt-daemon-system, arping, vlan, bridge-utils, dnsmasq, nftables, pdns-server, pdns-backend-pgsql
Suggests: pvc-client-api, pvc-client-cli Suggests: pvc-client-api, pvc-client-cli
Description: Parallel Virtual Cluster node daemon (Python 3) Description: Parallel Virtual Cluster node daemon (Python 3)
A KVM/Zookeeper/Ceph-based VM and private cloud manager A KVM/Zookeeper/Ceph-based VM and private cloud manager

View File

@ -19,11 +19,6 @@ To get started with PVC, read the [Cluster Architecture document](https://parall
## Changelog ## Changelog
#### v0.9.1
* Added per-VM migration method feature
* Fixed bug with provisioner system template listing
#### v0.9.0 #### v0.9.0
Numerous small improvements and bugfixes. This release is suitable for general use and is pre-release-quality software. Numerous small improvements and bugfixes. This release is suitable for general use and is pre-release-quality software.

View File

@ -21,7 +21,7 @@
############################################################################### ###############################################################################
# Version string for startup output # Version string for startup output
version = '0.9.1' version = '0.9.0'
import kazoo.client import kazoo.client
import libvirt import libvirt

View File

@ -113,11 +113,7 @@ class VMInstance(object):
try: try:
self.pinpolicy = zkhandler.readdata(self.zk_conn, '/domains/{}/pinpolicy'.format(self.domuuid)) self.pinpolicy = zkhandler.readdata(self.zk_conn, '/domains/{}/pinpolicy'.format(self.domuuid))
except: except:
self.pinpolicy = "none" self.pinpolicy = "None"
try:
self.migration_method = zkhandler.readdata(self.zk_conn, '/domains/{}/migration_method'.format(self.domuuid))
except:
self.migration_method = 'none'
# These will all be set later # These will all be set later
self.instart = False self.instart = False
@ -353,16 +349,11 @@ class VMInstance(object):
zkhandler.writedata(self.zk_conn, { '/domains/{}/state'.format(self.domuuid): 'start' }) zkhandler.writedata(self.zk_conn, { '/domains/{}/state'.format(self.domuuid): 'start' })
# Migrate the VM to a target host # Migrate the VM to a target host
def migrate_vm(self, force_live=False, force_shutdown=False): def migrate_vm(self, force_live=False):
# Wait for any previous migration # Wait for any previous migration
while self.inmigrate: while self.inmigrate:
time.sleep(0.1) time.sleep(0.1)
if self.migration_method == 'live':
force_live = True
elif self.migration_method == 'shutdown':
force_shutdown = True
self.inmigrate = True self.inmigrate = True
self.logger.out('Migrating VM to node "{}"'.format(self.node), state='i', prefix='Domain {}'.format(self.domuuid)) self.logger.out('Migrating VM to node "{}"'.format(self.node), state='i', prefix='Domain {}'.format(self.domuuid))
@ -475,20 +466,17 @@ class VMInstance(object):
abort_migrate('Target node changed during preparation') abort_migrate('Target node changed during preparation')
return return
if not force_shutdown: # A live migrate is attemped 3 times in succession
# A live migrate is attemped 3 times in succession ticks = 0
ticks = 0 while True:
while True: ticks += 1
ticks += 1 self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid))
self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid)) migrate_live_result = migrate_live()
migrate_live_result = migrate_live() if migrate_live_result:
if migrate_live_result: break
break time.sleep(0.5)
time.sleep(0.5) if ticks > 2:
if ticks > 2: break
break
else:
migrate_live_result = False
if not migrate_live_result: if not migrate_live_result:
if force_live: if force_live:

View File

@ -35,7 +35,7 @@ def fenceNode(node_name, zk_conn, config, logger):
failcount = 0 failcount = 0
while failcount < failcount_limit: while failcount < failcount_limit:
# Wait 5 seconds # Wait 5 seconds
time.sleep(config['keepalive_interval']) time.sleep(config.keepalive_interval)
# Get the state # Get the state
node_daemon_state = zkhandler.readdata(zk_conn, '/nodes/{}/daemonstate'.format(node_name)) node_daemon_state = zkhandler.readdata(zk_conn, '/nodes/{}/daemonstate'.format(node_name))
# Is it still 'dead' # Is it still 'dead'
@ -57,7 +57,7 @@ def fenceNode(node_name, zk_conn, config, logger):
# Shoot it in the head # Shoot it in the head
fence_status = rebootViaIPMI(ipmi_hostname, ipmi_username, ipmi_password, logger) fence_status = rebootViaIPMI(ipmi_hostname, ipmi_username, ipmi_password, logger)
# Hold to ensure the fence takes effect and system stabilizes # Hold to ensure the fence takes effect and system stabilizes
time.sleep(config['keepalive_interval'] * 2) time.sleep(config.keepalive_interval * 2)
# Force into secondary network state if needed # Force into secondary network state if needed
if node_name in config['coordinators']: if node_name in config['coordinators']: