Support per-VM migration type selectors
Allow a VM to specify its migration type as a default choice. The valid options are "default" (i.e. behave as now), "live" which forces a live migration only, and "shutdown" which forces a shutdown migration only. The new option is treated as a VM meta option and is set to default if not found.
This commit is contained in:
parent
d2c0d868c4
commit
ec0b8acf90
|
@ -0,0 +1,28 @@
|
|||
"""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 ###
|
|
@ -834,6 +834,9 @@ class API_VM_Root(Resource):
|
|||
node_autostart:
|
||||
type: boolean
|
||||
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:
|
||||
type: string
|
||||
description: The description of the VM
|
||||
|
@ -1036,6 +1039,7 @@ class API_VM_Root(Resource):
|
|||
{ 'name': 'node' },
|
||||
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
|
||||
{ '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" },
|
||||
])
|
||||
@Authenticator
|
||||
|
@ -1077,6 +1081,16 @@ class API_VM_Root(Resource):
|
|||
type: boolean
|
||||
required: false
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -1094,7 +1108,8 @@ class API_VM_Root(Resource):
|
|||
reqargs.get('node', None),
|
||||
reqargs.get('limit', None),
|
||||
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')
|
||||
|
||||
|
@ -1125,6 +1140,7 @@ class API_VM_Element(Resource):
|
|||
{ 'name': 'node' },
|
||||
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
|
||||
{ '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" },
|
||||
])
|
||||
@Authenticator
|
||||
|
@ -1168,6 +1184,16 @@ class API_VM_Element(Resource):
|
|||
type: boolean
|
||||
required: false
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -1185,7 +1211,8 @@ class API_VM_Element(Resource):
|
|||
reqargs.get('node', None),
|
||||
reqargs.get('limit', None),
|
||||
reqargs.get('selector', 'mem'),
|
||||
bool(strtobool(reqargs.get('autostart', 'false')))
|
||||
bool(strtobool(reqargs.get('autostart', 'false'))),
|
||||
reqargs.get('migration_method', 'none')
|
||||
)
|
||||
|
||||
@RequestParser([
|
||||
|
@ -1296,6 +1323,9 @@ class API_VM_Metadata(Resource):
|
|||
node_autostart:
|
||||
type: string
|
||||
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:
|
||||
description: Not found
|
||||
schema:
|
||||
|
@ -1309,6 +1339,7 @@ class API_VM_Metadata(Resource):
|
|||
{ 'name': 'selector', 'choices': ('mem', 'vcpus', 'load', 'vms'), 'helptext': "A valid selector must be specified" },
|
||||
{ 'name': 'autostart' },
|
||||
{ 'name': 'profile' },
|
||||
{ 'name': 'migration_method', 'choices': ('live', 'shutdown', 'none'), 'helptext': "A valid migration_method must be specified" },
|
||||
])
|
||||
@Authenticator
|
||||
def post(self, vm, reqargs):
|
||||
|
@ -1343,6 +1374,16 @@ class API_VM_Metadata(Resource):
|
|||
type: string
|
||||
required: false
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -1360,7 +1401,8 @@ class API_VM_Metadata(Resource):
|
|||
reqargs.get('limit', None),
|
||||
reqargs.get('selector', 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')
|
||||
|
||||
|
@ -4057,6 +4099,9 @@ class API_Provisioner_Template_System_Root(Resource):
|
|||
node_autostart:
|
||||
type: boolean
|
||||
description: Whether to start VM with node ready state (one-time)
|
||||
migration_method:
|
||||
type: string
|
||||
description: The preferred migration method (live, shutdown, none)
|
||||
parameters:
|
||||
- in: query
|
||||
name: limit
|
||||
|
@ -4084,7 +4129,8 @@ class API_Provisioner_Template_System_Root(Resource):
|
|||
{ 'name': 'vnc_bind' },
|
||||
{ 'name': 'node_limit' },
|
||||
{ 'name': 'node_selector' },
|
||||
{ 'name': 'node_autostart' }
|
||||
{ 'name': 'node_autostart' },
|
||||
{ 'name': 'migration_method' }
|
||||
])
|
||||
@Authenticator
|
||||
def post(self, reqargs):
|
||||
|
@ -4139,6 +4185,11 @@ class API_Provisioner_Template_System_Root(Resource):
|
|||
type: boolean
|
||||
required: false
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -4185,7 +4236,8 @@ class API_Provisioner_Template_System_Root(Resource):
|
|||
vnc_bind,
|
||||
reqargs.get('node_limit', 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')
|
||||
|
||||
|
@ -4222,7 +4274,8 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
{ 'name': 'vnc_bind' },
|
||||
{ 'name': 'node_limit' },
|
||||
{ 'name': 'node_selector' },
|
||||
{ 'name': 'node_autostart' }
|
||||
{ 'name': 'node_autostart' },
|
||||
{ 'name': 'migration_method' }
|
||||
])
|
||||
@Authenticator
|
||||
def post(self, template, reqargs):
|
||||
|
@ -4272,6 +4325,11 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
type: boolean
|
||||
required: false
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -4318,7 +4376,8 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
vnc_bind,
|
||||
reqargs.get('node_limit', None),
|
||||
reqargs.get('node_selector', None),
|
||||
node_autostart
|
||||
node_autostart,
|
||||
reqargs.get('migration_method', None),
|
||||
)
|
||||
|
||||
@RequestParser([
|
||||
|
@ -4329,7 +4388,8 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
{ 'name': 'vnc_bind' },
|
||||
{ 'name': 'node_limit' },
|
||||
{ 'name': 'node_selector' },
|
||||
{ 'name': 'node_autostart' }
|
||||
{ 'name': 'node_autostart' },
|
||||
{ 'name': 'migration_method' }
|
||||
])
|
||||
@Authenticator
|
||||
def put(self, template, reqargs):
|
||||
|
@ -4371,6 +4431,10 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
name: node_autostart
|
||||
type: boolean
|
||||
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:
|
||||
200:
|
||||
description: OK
|
||||
|
@ -4392,7 +4456,8 @@ class API_Provisioner_Template_System_Element(Resource):
|
|||
reqargs.get('vnc_bind'),
|
||||
reqargs.get('node_limit', None),
|
||||
reqargs.get('node_selector', None),
|
||||
reqargs.get('node_autostart', None)
|
||||
reqargs.get('node_autostart', None),
|
||||
reqargs.get('migration_method', None)
|
||||
)
|
||||
|
||||
@Authenticator
|
||||
|
|
|
@ -431,7 +431,7 @@ def vm_list(node=None, state=None, limit=None, is_fuzzy=True):
|
|||
|
||||
return retdata, retcode
|
||||
|
||||
def vm_define(xml, node, limit, selector, autostart):
|
||||
def vm_define(xml, node, limit, selector, autostart, migration_method):
|
||||
"""
|
||||
Define a VM from Libvirt XML in the PVC cluster.
|
||||
"""
|
||||
|
@ -443,7 +443,7 @@ def vm_define(xml, node, limit, selector, autostart):
|
|||
return { 'message': 'XML is malformed or incorrect: {}'.format(e) }, 400
|
||||
|
||||
zk_conn = pvc_common.startZKConnection(config['coordinators'])
|
||||
retflag, retdata = pvc_vm.define_vm(zk_conn, new_cfg, node, limit, selector, autostart, profile=None)
|
||||
retflag, retdata = pvc_vm.define_vm(zk_conn, new_cfg, node, limit, selector, autostart, migration_method, profile=None)
|
||||
pvc_common.stopZKConnection(zk_conn)
|
||||
|
||||
if retflag:
|
||||
|
@ -475,7 +475,8 @@ def get_vm_meta(vm):
|
|||
'name': vm,
|
||||
'node_limit': retdata['node_limit'],
|
||||
'node_selector': retdata['node_selector'],
|
||||
'node_autostart': retdata['node_autostart']
|
||||
'node_autostart': retdata['node_autostart'],
|
||||
'migration_method': retdata['migration_method']
|
||||
}
|
||||
else:
|
||||
retcode = 404
|
||||
|
@ -490,7 +491,7 @@ def get_vm_meta(vm):
|
|||
|
||||
return retdata, retcode
|
||||
|
||||
def update_vm_meta(vm, limit, selector, autostart, provisioner_profile):
|
||||
def update_vm_meta(vm, limit, selector, autostart, provisioner_profile, migration_method):
|
||||
"""
|
||||
Update metadata of a VM.
|
||||
"""
|
||||
|
@ -500,7 +501,7 @@ def update_vm_meta(vm, limit, selector, autostart, provisioner_profile):
|
|||
autostart = bool(strtobool(autostart))
|
||||
except:
|
||||
autostart = False
|
||||
retflag, retdata = pvc_vm.modify_vm_metadata(zk_conn, vm, limit, selector, autostart, provisioner_profile)
|
||||
retflag, retdata = pvc_vm.modify_vm_metadata(zk_conn, vm, limit, selector, autostart, provisioner_profile, migration_method)
|
||||
pvc_common.stopZKConnection(zk_conn)
|
||||
|
||||
if retflag:
|
||||
|
|
|
@ -35,9 +35,10 @@ class DBSystemTemplate(db.Model):
|
|||
node_limit = db.Column(db.Text)
|
||||
node_selector = db.Column(db.Text)
|
||||
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)
|
||||
|
||||
def __init__(self, name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, ova=None):
|
||||
def __init__(self, name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova=None):
|
||||
self.name = name
|
||||
self.vcpu_count = vcpu_count
|
||||
self.vram_mb = vram_mb
|
||||
|
@ -47,6 +48,7 @@ class DBSystemTemplate(db.Model):
|
|||
self.node_limit = node_limit
|
||||
self.node_selector = node_selector
|
||||
self.node_autostart = node_autostart
|
||||
self.migration_method = migration_method
|
||||
self.ova = ova
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -214,14 +214,14 @@ def template_list(limit):
|
|||
#
|
||||
# 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, 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, migration_method=None, ova=None):
|
||||
if list_template_system(name, is_fuzzy=False)[-1] != 404:
|
||||
retmsg = { 'message': 'The system template "{}" already exists.'.format(name) }
|
||||
retcode = 400
|
||||
return retmsg, retcode
|
||||
|
||||
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, ova)
|
||||
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);"
|
||||
args = (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova)
|
||||
|
||||
conn, cur = open_database(config)
|
||||
try:
|
||||
|
@ -359,7 +359,7 @@ def create_template_storage_element(name, disk_id, pool, source_volume=None, dis
|
|||
#
|
||||
# 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):
|
||||
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):
|
||||
if list_profile(name, is_fuzzy=False)[-1] != 200:
|
||||
retmsg = { 'message': 'The system template "{}" does not exist.'.format(name) }
|
||||
retcode = 400
|
||||
|
@ -420,6 +420,9 @@ def modify_template_system(name, vcpu_count=None, vram_mb=None, serial=None, vnc
|
|||
retcode = 400
|
||||
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)
|
||||
try:
|
||||
for field in fields:
|
||||
|
@ -1403,7 +1406,8 @@ def create_vm(self, vm_name, vm_profile, define_vm=True, start_vm=True, script_r
|
|||
node_limit = node_limit.split(',')
|
||||
node_selector = vm_data['system_details']['node_selector']
|
||||
node_autostart = vm_data['system_details']['node_autostart']
|
||||
retcode, retmsg = pvc_vm.define_vm(zk_conn, vm_schema.strip(), target_node, node_limit, node_selector, node_autostart, vm_profile, initial_state='provision')
|
||||
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, migration_method, vm_profile, initial_state='provision')
|
||||
print(retmsg)
|
||||
else:
|
||||
print("Skipping VM definition")
|
||||
|
|
|
@ -717,9 +717,10 @@ def format_list_template_system(template_data):
|
|||
template_serial_length = 7
|
||||
template_vnc_length = 4
|
||||
template_vnc_bind_length = 10
|
||||
template_node_limit_length = 9
|
||||
template_node_selector_length = 11
|
||||
template_node_autostart_length = 11
|
||||
template_node_limit_length = 6
|
||||
template_node_selector_length = 9
|
||||
template_node_autostart_length = 10
|
||||
template_migration_method_length = 10
|
||||
|
||||
for template in template_data:
|
||||
# template_name column
|
||||
|
@ -762,17 +763,22 @@ def format_list_template_system(template_data):
|
|||
_template_node_autostart_length = len(str(template['node_autostart'])) + 1
|
||||
if _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)
|
||||
template_list_output_header = '{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \
|
||||
{template_vcpu: <{template_vcpu_length}} \
|
||||
{template_vram: <{template_vram_length}} \
|
||||
Consoles: {template_serial: <{template_serial_length}} \
|
||||
Console: {template_serial: <{template_serial_length}} \
|
||||
{template_vnc: <{template_vnc_length}} \
|
||||
{template_vnc_bind: <{template_vnc_bind_length}} \
|
||||
Metadata: {template_node_limit: <{template_node_limit_length}} \
|
||||
Meta: {template_node_limit: <{template_node_limit_length}} \
|
||||
{template_node_selector: <{template_node_selector_length}} \
|
||||
{template_node_autostart: <{template_node_autostart_length}}{end_bold}'.format(
|
||||
{template_node_autostart: <{template_node_autostart_length}} \
|
||||
{template_migration_method: <{template_migration_method_length}}{end_bold}'.format(
|
||||
template_name_length=template_name_length,
|
||||
template_id_length=template_id_length,
|
||||
template_vcpu_length=template_vcpu_length,
|
||||
|
@ -783,6 +789,7 @@ Metadata: {template_node_limit: <{template_node_limit_length}} \
|
|||
template_node_limit_length=template_node_limit_length,
|
||||
template_node_selector_length=template_node_selector_length,
|
||||
template_node_autostart_length=template_node_autostart_length,
|
||||
template_migration_method_length=template_migration_method_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
template_state_colour='',
|
||||
|
@ -796,7 +803,8 @@ Metadata: {template_node_limit: <{template_node_limit_length}} \
|
|||
template_vnc_bind='VNC bind',
|
||||
template_node_limit='Limit',
|
||||
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
|
||||
|
@ -808,12 +816,13 @@ Metadata: {template_node_limit: <{template_node_limit_length}} \
|
|||
'{bold}{template_name: <{template_name_length}} {template_id: <{template_id_length}} \
|
||||
{template_vcpu: <{template_vcpu_length}} \
|
||||
{template_vram: <{template_vram_length}} \
|
||||
{template_serial: <{template_serial_length}} \
|
||||
{template_serial: <{template_serial_length}} \
|
||||
{template_vnc: <{template_vnc_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_autostart: <{template_node_autostart_length}}{end_bold}'.format(
|
||||
{template_node_autostart: <{template_node_autostart_length}} \
|
||||
{template_migration_method: <{template_migration_method_length}}{end_bold}'.format(
|
||||
template_name_length=template_name_length,
|
||||
template_id_length=template_id_length,
|
||||
template_vcpu_length=template_vcpu_length,
|
||||
|
@ -824,6 +833,7 @@ Metadata: {template_node_limit: <{template_node_limit_length}} \
|
|||
template_node_limit_length=template_node_limit_length,
|
||||
template_node_selector_length=template_node_selector_length,
|
||||
template_node_autostart_length=template_node_autostart_length,
|
||||
template_migration_method_length=template_migration_method_length,
|
||||
bold='',
|
||||
end_bold='',
|
||||
template_name=str(template['name']),
|
||||
|
@ -835,7 +845,8 @@ Metadata: {template_node_limit: <{template_node_limit_length}} \
|
|||
template_vnc_bind=str(template['vnc_bind']),
|
||||
template_node_limit=str(template['node_limit']),
|
||||
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'])
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -80,19 +80,20 @@ def vm_list(config, limit, target_node, target_state):
|
|||
else:
|
||||
return False, response.json().get('message', '')
|
||||
|
||||
def vm_define(config, xml, node, node_limit, node_selector, node_autostart):
|
||||
def vm_define(config, xml, node, node_limit, node_selector, node_autostart, migration_method):
|
||||
"""
|
||||
Define a new VM on the cluster
|
||||
|
||||
API endpoint: POST /vm
|
||||
API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}
|
||||
API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}, migration_method={migration_method}
|
||||
API schema: {"message":"{data}"}
|
||||
"""
|
||||
params = {
|
||||
'node': node,
|
||||
'limit': node_limit,
|
||||
'selector': node_selector,
|
||||
'autostart': node_autostart
|
||||
'autostart': node_autostart,
|
||||
'migration_method': migration_method
|
||||
}
|
||||
data = {
|
||||
'xml': xml
|
||||
|
@ -129,12 +130,12 @@ def vm_modify(config, vm, xml, restart):
|
|||
|
||||
return retstatus, response.json().get('message', '')
|
||||
|
||||
def vm_metadata(config, vm, node_limit, node_selector, node_autostart, provisioner_profile):
|
||||
def vm_metadata(config, vm, node_limit, node_selector, node_autostart, migration_method, provisioner_profile):
|
||||
"""
|
||||
Modify PVC metadata of a VM
|
||||
|
||||
API endpoint: GET /vm/{vm}/meta, POST /vm/{vm}/meta
|
||||
API arguments: limit={node_limit}, selector={node_selector}, autostart={node_autostart}, profile={provisioner_profile}
|
||||
API arguments: limit={node_limit}, selector={node_selector}, autostart={node_autostart}, migration_method={migration_method} profile={provisioner_profile}
|
||||
API schema: {"message":"{data}"}
|
||||
"""
|
||||
params = dict()
|
||||
|
@ -149,6 +150,9 @@ def vm_metadata(config, vm, node_limit, node_selector, node_autostart, provision
|
|||
if node_autostart is not None:
|
||||
params['autostart'] = node_autostart
|
||||
|
||||
if migration_method is not None:
|
||||
params['migration_method'] = migration_method
|
||||
|
||||
if provisioner_profile is not None:
|
||||
params['profile'] = provisioner_profile
|
||||
|
||||
|
@ -391,24 +395,30 @@ def format_info(config, domain_information, long_output):
|
|||
ainformation.append('')
|
||||
ainformation.append('{}Failure reason:{} {}'.format(ansiprint.purple(), ansiprint.end(), domain_information['failed_reason']))
|
||||
|
||||
if not domain_information['node_selector']:
|
||||
if not domain_information.get('node_selector'):
|
||||
formatted_node_selector = "False"
|
||||
else:
|
||||
formatted_node_selector = domain_information['node_selector']
|
||||
|
||||
if not domain_information['node_limit']:
|
||||
if not domain_information.get('node_limit'):
|
||||
formatted_node_limit = "False"
|
||||
else:
|
||||
formatted_node_limit = ', '.join(domain_information['node_limit'])
|
||||
|
||||
if not domain_information['node_autostart']:
|
||||
if not domain_information.get('node_autostart'):
|
||||
formatted_node_autostart = "False"
|
||||
else:
|
||||
formatted_node_autostart = domain_information['node_autostart']
|
||||
|
||||
if not domain_information.get('migration_method'):
|
||||
formatted_migration_method = "default"
|
||||
else:
|
||||
formatted_migration_method = domain_information['migration_method']
|
||||
|
||||
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('{}Autostart:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_autostart))
|
||||
ainformation.append('{}Migration Method:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_migration_method))
|
||||
|
||||
# Network list
|
||||
net_list = []
|
||||
|
|
|
@ -593,11 +593,16 @@ def cli_vm():
|
|||
'-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.'
|
||||
)
|
||||
@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(
|
||||
'vmconfig', type=click.File()
|
||||
)
|
||||
@cluster_req
|
||||
def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart):
|
||||
def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart, migration_method):
|
||||
"""
|
||||
Define a new virtual machine from Libvirt XML configuration file VMCONFIG.
|
||||
"""
|
||||
|
@ -613,7 +618,7 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart):
|
|||
except:
|
||||
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)
|
||||
retcode, retmsg = pvc_vm.vm_define(config, new_cfg, target_node, node_limit, node_selector, node_autostart, migration_method)
|
||||
cleanup(retcode, retmsg)
|
||||
|
||||
###############################################################################
|
||||
|
@ -633,6 +638,11 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart):
|
|||
'-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.'
|
||||
)
|
||||
@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(
|
||||
'-p', '--profile', 'provisioner_profile', default=None, show_default=False,
|
||||
help='PVC provisioner profile name for VM.'
|
||||
|
@ -641,15 +651,15 @@ def vm_define(vmconfig, target_node, node_limit, node_selector, node_autostart):
|
|||
'domain'
|
||||
)
|
||||
@cluster_req
|
||||
def vm_meta(domain, node_limit, node_selector, node_autostart, provisioner_profile):
|
||||
def vm_meta(domain, node_limit, node_selector, node_autostart, migration_method, 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.
|
||||
"""
|
||||
|
||||
if node_limit is None and node_selector is None and node_autostart is None and provisioner_profile is None:
|
||||
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:
|
||||
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, provisioner_profile)
|
||||
retcode, retmsg = pvc_vm.vm_metadata(config, domain, node_limit, node_selector, node_autostart, migration_method, provisioner_profile)
|
||||
cleanup(retcode, retmsg)
|
||||
|
||||
###############################################################################
|
||||
|
@ -2301,8 +2311,14 @@ def provisioner_template_system_list(limit):
|
|||
is_flag=True, default=False,
|
||||
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
|
||||
def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart):
|
||||
def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method):
|
||||
"""
|
||||
Add a new system template NAME to the PVC cluster provisioner.
|
||||
"""
|
||||
|
@ -2320,6 +2336,8 @@ def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, no
|
|||
params['node_selector'] = node_selector
|
||||
if 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')
|
||||
cleanup(retcode, retdata)
|
||||
|
@ -2369,8 +2387,14 @@ def provisioner_template_system_add(name, vcpus, vram, serial, vnc, vnc_bind, no
|
|||
is_flag=True, default=None,
|
||||
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
|
||||
def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart):
|
||||
def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method):
|
||||
"""
|
||||
Add a new system template NAME to the PVC cluster provisioner.
|
||||
"""
|
||||
|
@ -2383,6 +2407,7 @@ def provisioner_template_system_modify(name, vcpus, vram, serial, vnc, vnc_bind,
|
|||
params['node_limit'] = node_limit
|
||||
params['node_selector'] = node_selector
|
||||
params['node_autostart'] = node_autostart
|
||||
params['migration_method'] = migration_method
|
||||
|
||||
retcode, retdata = pvc_provisioner.template_modify(config, params, name, template_type='system')
|
||||
cleanup(retcode, retdata)
|
||||
|
|
|
@ -236,6 +236,10 @@ def getInformationFromXML(zk_conn, uuid):
|
|||
domain_node_autostart = zkhandler.readdata(zk_conn, '/domains/{}/node_autostart'.format(uuid))
|
||||
except:
|
||||
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:
|
||||
domain_node_limit = None
|
||||
|
@ -282,6 +286,7 @@ def getInformationFromXML(zk_conn, uuid):
|
|||
'node_limit': domain_node_limit,
|
||||
'node_selector': domain_node_selector,
|
||||
'node_autostart': bool(strtobool(domain_node_autostart)),
|
||||
'migration_method': domain_migration_method,
|
||||
'description': domain_description,
|
||||
'profile': domain_profile,
|
||||
'memory': int(domain_memory),
|
||||
|
|
|
@ -157,7 +157,7 @@ def flush_locks(zk_conn, domain):
|
|||
|
||||
return success, message
|
||||
|
||||
def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart, profile=None, initial_state='stop'):
|
||||
def define_vm(zk_conn, config_data, target_node, node_limit, node_selector, node_autostart, migration_method=None, profile=None, initial_state='stop'):
|
||||
# Parse the XML data
|
||||
try:
|
||||
parsed_xml = lxml.objectify.fromstring(config_data)
|
||||
|
@ -206,6 +206,7 @@ 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_selector'.format(dom_uuid): node_selector,
|
||||
'/domains/{}/node_autostart'.format(dom_uuid): node_autostart,
|
||||
'/domains/{}/migration_method'.format(dom_uuid): migration_method,
|
||||
'/domains/{}/failedreason'.format(dom_uuid): '',
|
||||
'/domains/{}/consolelog'.format(dom_uuid): '',
|
||||
'/domains/{}/rbdlist'.format(dom_uuid): formatted_rbd_list,
|
||||
|
@ -215,7 +216,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)
|
||||
|
||||
def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostart, provisioner_profile):
|
||||
def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostart, provisioner_profile, migration_method):
|
||||
dom_uuid = getDomainUUID(zk_conn, domain)
|
||||
if not dom_uuid:
|
||||
return False, 'ERROR: Could not find VM "{}" in the cluster!'.format(domain)
|
||||
|
@ -240,6 +241,11 @@ def modify_vm_metadata(zk_conn, domain, node_limit, node_selector, node_autostar
|
|||
'/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)
|
||||
|
||||
def modify_vm(zk_conn, domain, restart, new_vm_config):
|
||||
|
@ -781,9 +787,15 @@ def format_info(zk_conn, domain_information, long_output):
|
|||
else:
|
||||
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('{}Node limit:{} {}'.format(ansiprint.purple(), ansiprint.end(), formatted_node_limit))
|
||||
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
|
||||
net_list = []
|
||||
|
|
|
@ -113,7 +113,11 @@ class VMInstance(object):
|
|||
try:
|
||||
self.pinpolicy = zkhandler.readdata(self.zk_conn, '/domains/{}/pinpolicy'.format(self.domuuid))
|
||||
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
|
||||
self.instart = False
|
||||
|
@ -349,11 +353,16 @@ class VMInstance(object):
|
|||
zkhandler.writedata(self.zk_conn, { '/domains/{}/state'.format(self.domuuid): 'start' })
|
||||
|
||||
# Migrate the VM to a target host
|
||||
def migrate_vm(self, force_live=False):
|
||||
def migrate_vm(self, force_live=False, force_shutdown=False):
|
||||
# Wait for any previous migration
|
||||
while self.inmigrate:
|
||||
time.sleep(0.1)
|
||||
|
||||
if self.migration_method == 'live':
|
||||
force_live = True
|
||||
elif self.migration_method == 'shutdown':
|
||||
force_shutdown = True
|
||||
|
||||
self.inmigrate = True
|
||||
self.logger.out('Migrating VM to node "{}"'.format(self.node), state='i', prefix='Domain {}'.format(self.domuuid))
|
||||
|
||||
|
@ -466,17 +475,20 @@ class VMInstance(object):
|
|||
abort_migrate('Target node changed during preparation')
|
||||
return
|
||||
|
||||
# A live migrate is attemped 3 times in succession
|
||||
ticks = 0
|
||||
while True:
|
||||
ticks += 1
|
||||
self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid))
|
||||
migrate_live_result = migrate_live()
|
||||
if migrate_live_result:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
if ticks > 2:
|
||||
break
|
||||
if not force_shutdown:
|
||||
# A live migrate is attemped 3 times in succession
|
||||
ticks = 0
|
||||
while True:
|
||||
ticks += 1
|
||||
self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid))
|
||||
migrate_live_result = migrate_live()
|
||||
if migrate_live_result:
|
||||
break
|
||||
time.sleep(0.5)
|
||||
if ticks > 2:
|
||||
break
|
||||
else:
|
||||
migrate_live_result = False
|
||||
|
||||
if not migrate_live_result:
|
||||
if force_live:
|
||||
|
|
Loading…
Reference in New Issue