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:
|
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
|
||||||
|
@ -1036,6 +1039,7 @@ 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
|
||||||
|
@ -1077,6 +1081,16 @@ 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
|
||||||
|
@ -1094,7 +1108,8 @@ 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')
|
||||||
|
|
||||||
|
@ -1125,6 +1140,7 @@ 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
|
||||||
|
@ -1168,6 +1184,16 @@ 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
|
||||||
|
@ -1185,7 +1211,8 @@ 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([
|
||||||
|
@ -1296,6 +1323,9 @@ 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:
|
||||||
|
@ -1309,6 +1339,7 @@ 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):
|
||||||
|
@ -1343,6 +1374,16 @@ 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
|
||||||
|
@ -1360,7 +1401,8 @@ 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')
|
||||||
|
|
||||||
|
@ -4057,6 +4099,9 @@ 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
|
||||||
|
@ -4084,7 +4129,8 @@ 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):
|
||||||
|
@ -4139,6 +4185,11 @@ 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
|
||||||
|
@ -4185,7 +4236,8 @@ 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')
|
||||||
|
|
||||||
|
@ -4222,7 +4274,8 @@ 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):
|
||||||
|
@ -4272,6 +4325,11 @@ 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
|
||||||
|
@ -4318,7 +4376,8 @@ 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([
|
||||||
|
@ -4329,7 +4388,8 @@ 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):
|
||||||
|
@ -4371,6 +4431,10 @@ 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
|
||||||
|
@ -4392,7 +4456,8 @@ 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
|
||||||
|
|
|
@ -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):
|
def vm_define(xml, node, limit, selector, autostart, migration_method):
|
||||||
"""
|
"""
|
||||||
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):
|
||||||
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, 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)
|
pvc_common.stopZKConnection(zk_conn)
|
||||||
|
|
||||||
if retflag:
|
if retflag:
|
||||||
|
@ -475,7 +475,8 @@ 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
|
||||||
|
@ -490,7 +491,7 @@ def get_vm_meta(vm):
|
||||||
|
|
||||||
return retdata, retcode
|
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.
|
Update metadata of a VM.
|
||||||
"""
|
"""
|
||||||
|
@ -500,7 +501,7 @@ def update_vm_meta(vm, limit, selector, autostart, provisioner_profile):
|
||||||
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)
|
retflag, retdata = pvc_vm.modify_vm_metadata(zk_conn, vm, limit, selector, autostart, provisioner_profile, migration_method)
|
||||||
pvc_common.stopZKConnection(zk_conn)
|
pvc_common.stopZKConnection(zk_conn)
|
||||||
|
|
||||||
if retflag:
|
if retflag:
|
||||||
|
|
|
@ -35,9 +35,10 @@ 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, 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.name = name
|
||||||
self.vcpu_count = vcpu_count
|
self.vcpu_count = vcpu_count
|
||||||
self.vram_mb = vram_mb
|
self.vram_mb = vram_mb
|
||||||
|
@ -47,6 +48,7 @@ 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):
|
||||||
|
|
|
@ -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, 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:
|
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, ova) VALUES (%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, 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, ova)
|
args = (name, vcpu_count, vram_mb, serial, vnc, vnc_bind, node_limit, node_selector, node_autostart, migration_method, ova)
|
||||||
|
|
||||||
conn, cur = open_database(config)
|
conn, cur = open_database(config)
|
||||||
try:
|
try:
|
||||||
|
@ -359,7 +359,7 @@ 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):
|
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:
|
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 = 400
|
retcode = 400
|
||||||
|
@ -420,6 +420,9 @@ 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:
|
||||||
|
@ -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_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']
|
||||||
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)
|
print(retmsg)
|
||||||
else:
|
else:
|
||||||
print("Skipping VM definition")
|
print("Skipping VM definition")
|
||||||
|
|
|
@ -717,9 +717,10 @@ 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 = 9
|
template_node_limit_length = 6
|
||||||
template_node_selector_length = 11
|
template_node_selector_length = 9
|
||||||
template_node_autostart_length = 11
|
template_node_autostart_length = 10
|
||||||
|
template_migration_method_length = 10
|
||||||
|
|
||||||
for template in template_data:
|
for template in template_data:
|
||||||
# template_name column
|
# template_name column
|
||||||
|
@ -762,17 +763,22 @@ 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}} \
|
||||||
Consoles: {template_serial: <{template_serial_length}} \
|
Console: {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}} \
|
||||||
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_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_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,
|
||||||
|
@ -783,6 +789,7 @@ Metadata: {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='',
|
||||||
|
@ -796,7 +803,8 @@ Metadata: {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
|
||||||
|
@ -808,12 +816,13 @@ Metadata: {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}}{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_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,
|
||||||
|
@ -824,6 +833,7 @@ Metadata: {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']),
|
||||||
|
@ -835,7 +845,8 @@ Metadata: {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'])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -80,19 +80,20 @@ 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):
|
def vm_define(config, xml, node, node_limit, node_selector, node_autostart, migration_method):
|
||||||
"""
|
"""
|
||||||
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}
|
API arguments: xml={xml}, node={node}, limit={node_limit}, selector={node_selector}, autostart={node_autostart}, migration_method={migration_method}
|
||||||
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
|
||||||
|
@ -129,12 +130,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, provisioner_profile):
|
def vm_metadata(config, vm, node_limit, node_selector, node_autostart, migration_method, 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}, 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}"}
|
API schema: {"message":"{data}"}
|
||||||
"""
|
"""
|
||||||
params = dict()
|
params = dict()
|
||||||
|
@ -149,6 +150,9 @@ def vm_metadata(config, vm, node_limit, node_selector, node_autostart, provision
|
||||||
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
|
||||||
|
|
||||||
|
@ -391,24 +395,30 @@ 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['node_selector']:
|
if not domain_information.get('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['node_limit']:
|
if not domain_information.get('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['node_autostart']:
|
if not domain_information.get('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 = "default"
|
||||||
|
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 = []
|
||||||
|
|
|
@ -593,11 +593,16 @@ 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):
|
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.
|
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:
|
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)
|
retcode, retmsg = pvc_vm.vm_define(config, new_cfg, target_node, node_limit, node_selector, node_autostart, migration_method)
|
||||||
cleanup(retcode, retmsg)
|
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,
|
'-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.'
|
||||||
|
@ -641,15 +651,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, 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.
|
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.')
|
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)
|
cleanup(retcode, retmsg)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -2301,8 +2311,14 @@ 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):
|
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.
|
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
|
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)
|
||||||
|
@ -2369,8 +2387,14 @@ 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):
|
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.
|
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_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)
|
||||||
|
|
|
@ -236,6 +236,10 @@ 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
|
||||||
|
@ -282,6 +286,7 @@ 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),
|
||||||
|
|
|
@ -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, 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
|
# Parse the XML data
|
||||||
try:
|
try:
|
||||||
parsed_xml = lxml.objectify.fromstring(config_data)
|
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_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,
|
||||||
|
@ -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)
|
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)
|
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)
|
||||||
|
@ -240,6 +241,11 @@ 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):
|
||||||
|
@ -781,9 +787,15 @@ 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 = []
|
||||||
|
|
|
@ -113,7 +113,11 @@ 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
|
||||||
|
@ -349,11 +353,16 @@ 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):
|
def migrate_vm(self, force_live=False, force_shutdown=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))
|
||||||
|
|
||||||
|
@ -466,17 +475,20 @@ class VMInstance(object):
|
||||||
abort_migrate('Target node changed during preparation')
|
abort_migrate('Target node changed during preparation')
|
||||||
return
|
return
|
||||||
|
|
||||||
# A live migrate is attemped 3 times in succession
|
if not force_shutdown:
|
||||||
ticks = 0
|
# A live migrate is attemped 3 times in succession
|
||||||
while True:
|
ticks = 0
|
||||||
ticks += 1
|
while True:
|
||||||
self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid))
|
ticks += 1
|
||||||
migrate_live_result = migrate_live()
|
self.logger.out('Attempting live migration try {}'.format(ticks), state='i', prefix='Domain {}'.format(self.domuuid))
|
||||||
if migrate_live_result:
|
migrate_live_result = migrate_live()
|
||||||
break
|
if migrate_live_result:
|
||||||
time.sleep(0.5)
|
break
|
||||||
if ticks > 2:
|
time.sleep(0.5)
|
||||||
break
|
if ticks > 2:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
migrate_live_result = False
|
||||||
|
|
||||||
if not migrate_live_result:
|
if not migrate_live_result:
|
||||||
if force_live:
|
if force_live:
|
||||||
|
|
Loading…
Reference in New Issue