458 lines
14 KiB
Python
458 lines
14 KiB
Python
|
|
# -*- coding: UTF-8 -*-
|
||
|
|
from .pool import Pool, PoolElement, Template, extractString
|
||
|
|
|
||
|
|
|
||
|
|
class History(Template):
|
||
|
|
def __repr__(self):
|
||
|
|
return '<oca.vm.History("seq={0}")>'.format(self.seq)
|
||
|
|
|
||
|
|
|
||
|
|
class VirtualMachine(PoolElement):
|
||
|
|
METHODS = {
|
||
|
|
'info': 'vm.info',
|
||
|
|
'allocate': 'vm.allocate',
|
||
|
|
'action': 'vm.action',
|
||
|
|
'migrate': 'vm.migrate',
|
||
|
|
'deploy': 'vm.deploy',
|
||
|
|
'savedisk': 'vm.savedisk',
|
||
|
|
'delete': 'vm.delete',
|
||
|
|
'chown': 'vm.chown',
|
||
|
|
'update': 'vm.update',
|
||
|
|
}
|
||
|
|
|
||
|
|
INIT = 0
|
||
|
|
PENDING = 1
|
||
|
|
HOLD = 2
|
||
|
|
ACTIVE = 3
|
||
|
|
STOPPED = 4
|
||
|
|
SUSPENDED = 5
|
||
|
|
DONE = 6
|
||
|
|
FAILED = 7
|
||
|
|
POWEROFF = 8
|
||
|
|
UNDEPLOYED = 9
|
||
|
|
VM_STATE = ['INIT', 'PENDING', 'HOLD', 'ACTIVE', 'STOPPED',
|
||
|
|
'SUSPENDED', 'DONE', 'FAILED', 'POWEROFF', 'UNDEPLOYED']
|
||
|
|
|
||
|
|
SHORT_VM_STATES = {
|
||
|
|
'INIT': 'init',
|
||
|
|
'PENDING': 'pend',
|
||
|
|
'HOLD': 'hold',
|
||
|
|
'ACTIVE': 'actv',
|
||
|
|
'STOPPED': 'stop',
|
||
|
|
'SUSPENDED': 'susp',
|
||
|
|
'DONE': 'done',
|
||
|
|
'FAILED': 'fail',
|
||
|
|
'POWEROFF': 'poff',
|
||
|
|
'UNDEPLOYED': 'udep'
|
||
|
|
}
|
||
|
|
|
||
|
|
LCM_STATE = ['LCM_INIT', 'PROLOG', 'BOOT', 'RUNNING', 'MIGRATE', 'SAVE_STOP', 'SAVE_SUSPEND',
|
||
|
|
'SAVE_MIGRATE', 'PROLOG_MIGRATE', 'PROLOG_RESUME', 'EPILOG_STOP', 'EPILOG',
|
||
|
|
'SHUTDOWN', 'CANCEL', 'FAILURE', 'CLEANUP_RESUBMIT', 'UNKNOWN', 'HOTPLUG',
|
||
|
|
'SHUTDOWN_POWEROFF', 'BOOT_UNKNOWN', 'BOOT_POWEROFF', 'BOOT_SUSPENDED',
|
||
|
|
'BOOT_STOPPED', 'CLEANUP_DELETE', 'HOTPLUG_SNAPSHOT', 'HOTPLUG_NIC',
|
||
|
|
'HOTPLUG_SAVEAS', 'HOTPLUG_SAVEAS_POWEROFF', 'HOTPLUG_SAVEAS_SUSPENDED',
|
||
|
|
'SHUTDOWN_UNDEPLOY', 'EPILOG_UNDEPLOY', 'PROLOG_UNDEPLOY', 'BOOT_UNDEPLOY',
|
||
|
|
'HOTPLUG_PROLOG_POWEROFF', 'HOTPLUG_EPILOG_POWEROFF', 'BOOT_MIGRATE',
|
||
|
|
'BOOT_FAILURE', 'BOOT_MIGRATE_FAILURE', 'PROLOG_MIGRATE_FAILURE',
|
||
|
|
'PROLOG_FAILURE', 'EPILOG_FAILURE', 'EPILOG_STOP_FAILURE',
|
||
|
|
'EPILOG_UNDEPLOY_FAILURE', 'PROLOG_MIGRATE_POWEROFF',
|
||
|
|
'PROLOG_MIGRATE_POWEROFF_FAILURE', 'PROLOG_MIGRATE_SUSPEND',
|
||
|
|
'PROLOG_MIGRATE_SUSPEND_FAILURE', 'BOOT_UNDEPLOY_FAILURE',
|
||
|
|
'BOOT_STOPPED_FAILURE', 'PROLOG_RESUME_FAILURE', 'PROLOG_UNDEPLOY_FAILURE',
|
||
|
|
'DISK_SNAPSHOT_POWEROFF', 'DISK_SNAPSHOT_REVERT_POWEROFF',
|
||
|
|
'DISK_SNAPSHOT_DELETE_POWEROFF', 'DISK_SNAPSHOT_SUSPENDED',
|
||
|
|
'DISK_SNAPSHOT_REVERT_SUSPENDED', 'DISK_SNAPSHOT_DELETE_SUSPENDED',
|
||
|
|
'DISK_SNAPSHOT', 'DISK_SNAPSHOT_REVERT', 'DISK_SNAPSHOT_DELETE']
|
||
|
|
|
||
|
|
SHORT_LCM_STATES = {
|
||
|
|
'LCM_INIT': 'lcm_i',
|
||
|
|
'PROLOG': 'prolo',
|
||
|
|
'BOOT': 'boot',
|
||
|
|
'RUNNING': 'running',
|
||
|
|
'MIGRATE': 'migrate',
|
||
|
|
'SAVE_STOP': 'save_stop',
|
||
|
|
'SAVE_SUSPEND': 'save_suspend',
|
||
|
|
'SAVE_MIGRATE': 'save_mig',
|
||
|
|
'PROLOG_MIGRATE': 'prolo',
|
||
|
|
'PROLOG_RESUME': 'prolo',
|
||
|
|
'EPILOG_STOP': 'epilo',
|
||
|
|
'EPILOG': 'epilo',
|
||
|
|
'SHUTDOWN': 'shutd',
|
||
|
|
'CANCEL': 'cance',
|
||
|
|
'FAILURE': 'failu',
|
||
|
|
'CLEANUP_RESUBMIT': 'clean',
|
||
|
|
'UNKNOWN': 'unkno',
|
||
|
|
'HOTPLUG': 'hotpl',
|
||
|
|
'SHUTDOWN_POWEROFF': 'shutd',
|
||
|
|
'BOOT_UNKNOWN': ':boot_u',
|
||
|
|
'BOOT_POWEROFF': 'boot_p',
|
||
|
|
'BOOT_SUSPENDED': 'boot_su',
|
||
|
|
'BOOT_STOPPED': 'boot_st',
|
||
|
|
'CLEANUP_DELETE': 'clean',
|
||
|
|
'HOTPLUG_SNAPSHOT': 'hotp_sn',
|
||
|
|
'HOTPLUG_NIC': 'hotp_n',
|
||
|
|
'HOTPLUG_SAVEAS': 'hot_sav',
|
||
|
|
'HOTPLUG_SAVEAS_POWEROFF': 'hot_sav_p',
|
||
|
|
'HOTPLUG_SAVEAS_SUSPENDED': 'hot_sav_sus',
|
||
|
|
'SHUTDOWN_UNDEPLOY': 'shut_un',
|
||
|
|
'EPILOG_UNDEPLOY': 'epi_und',
|
||
|
|
'PROLOG_UNDEPLOY': 'pro_und',
|
||
|
|
'BOOT_UNDEPLOY': 'boot_und',
|
||
|
|
'HOTPLUG_PROLOG_POWEROFF': 'hot_pro_pow',
|
||
|
|
'HOTPLUG_EPILOG_POWEROFF': 'hot_epi_pow',
|
||
|
|
'BOOT_MIGRATE': 'boot_mig',
|
||
|
|
'BOOT_FAILURE': 'boot_fail',
|
||
|
|
'BOOT_MIGRATE_FAILURE': 'boot_mig_fail',
|
||
|
|
'PROLOG_MIGRATE_FAILURE': 'pro_mig_fail',
|
||
|
|
'PROLOG_FAILURE': 'prolog_fail',
|
||
|
|
'EPILOG_FAILURE': 'epilog_fail',
|
||
|
|
'EPILOG_STOP_FAILURE': 'epilog_stop_fail',
|
||
|
|
'EPILOG_UNDEPLOY_FAILURE': 'epilog_undep_fail',
|
||
|
|
'PROLOG_MIGRATE_POWEROFF': 'prolog_migrate_power',
|
||
|
|
'PROLOG_MIGRATE_POWEROFF_FAILURE': 'prolog_migrate_power_fail',
|
||
|
|
'PROLOG_MIGRATE_SUSPEND': 'prolog_migrate_sus',
|
||
|
|
'PROLOG_MIGRATE_SUSPEND_FAILURE': 'prolog_migrate_sus_fail',
|
||
|
|
'BOOT_UNDEPLOY_FAILURE': 'boot_und_fail',
|
||
|
|
'BOOT_STOPPED_FAILURE': 'boot_stop_fail',
|
||
|
|
'PROLOG_RESUME_FAILURE': 'prolog_resume_fail',
|
||
|
|
'PROLOG_UNDEPLOY_FAILURE': 'prolog_unde_fail',
|
||
|
|
'DISK_SNAPSHOT_POWEROFF': 'disk_snap_poweroff',
|
||
|
|
'DISK_SNAPSHOT_REVERT_POWEROFF': 'disk_snap_rever_poweroff',
|
||
|
|
'DISK_SNAPSHOT_DELETE_POWEROFF': 'disk_snap_delete_poweroff',
|
||
|
|
'DISK_SNAPSHOT_SUSPENDED': 'disk_snap_suspend',
|
||
|
|
'DISK_SNAPSHOT_REVERT_SUSPENDED': 'disk_snap_revert_suspended',
|
||
|
|
'DISK_SNAPSHOT_DELETE_SUSPENDED': 'disk_snap_del_suspend',
|
||
|
|
'DISK_SNAPSHOT': 'disk_snap',
|
||
|
|
'DISK_SNAPSHOT_REVERT': 'disk_snap_revert',
|
||
|
|
'DISK_SNAPSHOT_DELETE': 'disk_snap_delete',
|
||
|
|
}
|
||
|
|
|
||
|
|
MIGRATE_REASON = ['NONE', 'ERROR', 'STOP_RESUME', 'USER', 'CANCEL']
|
||
|
|
|
||
|
|
SHORT_MIGRATE_REASON = {
|
||
|
|
'NONE': 'none',
|
||
|
|
'ERROR': 'erro',
|
||
|
|
'STOP_RESUME': 'stop',
|
||
|
|
'USER': 'user',
|
||
|
|
'CANCEL': 'canc'
|
||
|
|
}
|
||
|
|
|
||
|
|
XML_TYPES = {
|
||
|
|
'id': int,
|
||
|
|
'uid': int,
|
||
|
|
'gid': int,
|
||
|
|
'uname': extractString,
|
||
|
|
'gname': extractString,
|
||
|
|
'name': extractString,
|
||
|
|
# 'permissions': ???,
|
||
|
|
# 'last_poll': int,
|
||
|
|
'state': int,
|
||
|
|
'lcm_state': int,
|
||
|
|
'resched': int,
|
||
|
|
'stime': int,
|
||
|
|
'etime': int,
|
||
|
|
'deploy_id': extractString,
|
||
|
|
'template': ['TEMPLATE', Template, ['NIC', 'DISK']],
|
||
|
|
'user_template': ['USER_TEMPLATE', Template],
|
||
|
|
'history_records': ['HISTORY_RECORDS', lambda x: [History(i)
|
||
|
|
for i in x] if x is not None else []],
|
||
|
|
}
|
||
|
|
|
||
|
|
ELEMENT_NAME = 'VM'
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def allocate(client, template):
|
||
|
|
"""
|
||
|
|
allocates a virtual machine description from the given template string
|
||
|
|
|
||
|
|
Arguments
|
||
|
|
|
||
|
|
``template``
|
||
|
|
a string containing the template of the vm
|
||
|
|
"""
|
||
|
|
vm_id = client.call(VirtualMachine.METHODS['allocate'], template)
|
||
|
|
return vm_id
|
||
|
|
|
||
|
|
def __init__(self, xml, client):
|
||
|
|
super(VirtualMachine, self).__init__(xml, client)
|
||
|
|
self.id = self['ID'] if self['ID'] else None
|
||
|
|
|
||
|
|
def deploy(self, host_id):
|
||
|
|
"""
|
||
|
|
initiates the instance of the given vmid on the target host
|
||
|
|
|
||
|
|
Arguments
|
||
|
|
|
||
|
|
``host_id``
|
||
|
|
the host id (hid) of the target host where the VM will be
|
||
|
|
instantiated.
|
||
|
|
"""
|
||
|
|
self.client.call(self.METHODS['deploy'], self.id, host_id)
|
||
|
|
|
||
|
|
def migrate(self, dest_host):
|
||
|
|
"""
|
||
|
|
migrates virtual machine to the target host
|
||
|
|
|
||
|
|
Arguments
|
||
|
|
|
||
|
|
``dest_host``
|
||
|
|
the target host id
|
||
|
|
"""
|
||
|
|
self.client.call(self.METHODS['migrate'], self.id, dest_host, False)
|
||
|
|
|
||
|
|
def live_migrate(self, dest_host):
|
||
|
|
"""
|
||
|
|
live migrates virtual machine to the target host
|
||
|
|
|
||
|
|
Arguments
|
||
|
|
|
||
|
|
``dest_host``
|
||
|
|
the target host id
|
||
|
|
"""
|
||
|
|
self.client.call(self.METHODS['migrate'], self.id, dest_host, True)
|
||
|
|
|
||
|
|
def save_disk(self, disk_id, dest_disk):
|
||
|
|
"""
|
||
|
|
Sets the disk to be saved in the given image
|
||
|
|
|
||
|
|
Arguments
|
||
|
|
|
||
|
|
``disk_id``
|
||
|
|
disk id of the disk we want to save
|
||
|
|
``dest_disk``
|
||
|
|
image id where the disk will be saved.
|
||
|
|
"""
|
||
|
|
self.client.call(self.METHODS['savedisk'], self.id, disk_id, dest_disk)
|
||
|
|
|
||
|
|
def shutdown(self):
|
||
|
|
"""
|
||
|
|
Shutdowns an already deployed VM
|
||
|
|
"""
|
||
|
|
self._action('shutdown')
|
||
|
|
|
||
|
|
def shutdown_hard(self):
|
||
|
|
"""
|
||
|
|
Shutdown hard an already deployed VM
|
||
|
|
"""
|
||
|
|
self._action('shutdown-hard')
|
||
|
|
|
||
|
|
def poweroff(self):
|
||
|
|
"""
|
||
|
|
Power off an running vm
|
||
|
|
"""
|
||
|
|
self._action('poweroff')
|
||
|
|
|
||
|
|
def poweroff_hard(self):
|
||
|
|
"""
|
||
|
|
Power off hard an running vm
|
||
|
|
"""
|
||
|
|
self._action('poweroff-hard')
|
||
|
|
|
||
|
|
def cancel(self):
|
||
|
|
"""
|
||
|
|
Cancels a running VM
|
||
|
|
"""
|
||
|
|
self._action('cancel')
|
||
|
|
|
||
|
|
def hold(self):
|
||
|
|
"""
|
||
|
|
Sets a VM to hold state, scheduler will not deploy it
|
||
|
|
"""
|
||
|
|
self._action('hold')
|
||
|
|
|
||
|
|
def release(self):
|
||
|
|
"""
|
||
|
|
Releases a VM from hold state
|
||
|
|
"""
|
||
|
|
self._action('release')
|
||
|
|
|
||
|
|
def stop(self):
|
||
|
|
"""
|
||
|
|
Stops a running VM
|
||
|
|
"""
|
||
|
|
self._action('stop')
|
||
|
|
|
||
|
|
def suspend(self):
|
||
|
|
"""
|
||
|
|
Saves a running VM
|
||
|
|
"""
|
||
|
|
self._action('suspend')
|
||
|
|
|
||
|
|
def resume(self):
|
||
|
|
"""
|
||
|
|
Resumes the execution of a saved VM
|
||
|
|
"""
|
||
|
|
self._action('resume')
|
||
|
|
|
||
|
|
def finalize(self):
|
||
|
|
"""
|
||
|
|
Deletes a VM from the pool and DB
|
||
|
|
"""
|
||
|
|
self._action('finalize')
|
||
|
|
|
||
|
|
def reboot(self, hard=False):
|
||
|
|
"""
|
||
|
|
Reboot the VM. Optionally perform a hard reboot
|
||
|
|
"""
|
||
|
|
self._action('reboot-hard' if hard else 'reboot')
|
||
|
|
|
||
|
|
def resubmit(self):
|
||
|
|
"""
|
||
|
|
Redeploy the VM.
|
||
|
|
"""
|
||
|
|
self._action('resubmit')
|
||
|
|
|
||
|
|
def delete(self):
|
||
|
|
"""
|
||
|
|
Delete the VM.
|
||
|
|
"""
|
||
|
|
self._action('delete')
|
||
|
|
|
||
|
|
def resched(self):
|
||
|
|
"""
|
||
|
|
Set the rescheduling flag of the VM.
|
||
|
|
"""
|
||
|
|
self._action('resched')
|
||
|
|
|
||
|
|
def unresched(self):
|
||
|
|
"""
|
||
|
|
Remove the rescheduling flag of the VM.
|
||
|
|
"""
|
||
|
|
self._action('unresched')
|
||
|
|
|
||
|
|
def _action(self, action):
|
||
|
|
self.client.call(self.METHODS['action'], action, self.id)
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
return '<oca.VirtualMachine("%s")>' % self.name
|
||
|
|
|
||
|
|
@property
|
||
|
|
def str_state(self):
|
||
|
|
"""
|
||
|
|
String representation of virtual machine state.
|
||
|
|
One of: INIT, PENDING, HOLD, ACTIVE, STOPPED, SUSPENDED,
|
||
|
|
DONE, FAILED, POWEROFF, UNDEPLOYED
|
||
|
|
"""
|
||
|
|
return self.VM_STATE[int(self.state)]
|
||
|
|
|
||
|
|
@property
|
||
|
|
def short_state(self):
|
||
|
|
"""
|
||
|
|
Short string representation of virtual machine state.
|
||
|
|
One of: init, pend, hold, actv, stop, susp, done, fail, poff, udep
|
||
|
|
"""
|
||
|
|
return self.SHORT_VM_STATES[self.str_state]
|
||
|
|
|
||
|
|
@property
|
||
|
|
def str_lcm_state(self):
|
||
|
|
"""
|
||
|
|
String representation of virtual machine LCM state.
|
||
|
|
One of: LCM_INIT, PROLOG, BOOT, RUNNING, MIGRATE,
|
||
|
|
SAVE_STOP, SAVE_SUSPEND, SAVE_MIGRATE, PROLOG_MIGRATE,
|
||
|
|
PROLOG_RESUME, EPILOG_STOP, EPILOG, SHUTDOWN, CANCEL,
|
||
|
|
FAILURE, DELETE, UNKNOWN and others. See:
|
||
|
|
http://docs.opennebula.org/4.14/integration/system_interfaces/api.html#schemas-for-virtual-machine
|
||
|
|
"""
|
||
|
|
return self.LCM_STATE[int(self.lcm_state)]
|
||
|
|
|
||
|
|
@property
|
||
|
|
def short_lcm_state(self):
|
||
|
|
"""
|
||
|
|
Short string representation of virtual machine LCM state.
|
||
|
|
One of: init, prol, boot, runn, migr, save, save,
|
||
|
|
save, migr, prol, epil, shut, shut, fail,
|
||
|
|
dele, unkn
|
||
|
|
"""
|
||
|
|
return self.SHORT_LCM_STATES[self.str_lcm_state]
|
||
|
|
|
||
|
|
def update(self, template, merge=False):
|
||
|
|
"""
|
||
|
|
Update the template of this host. If merge is false (default),
|
||
|
|
the existing template is replaced.
|
||
|
|
"""
|
||
|
|
self.client.call(self.METHODS['update'], self.id, template, 1 if merge else 0)
|
||
|
|
|
||
|
|
|
||
|
|
class VirtualMachinePool(Pool):
|
||
|
|
METHODS = {
|
||
|
|
'info': 'vmpool.info',
|
||
|
|
'infoextended': 'vmpool.infoextended',
|
||
|
|
}
|
||
|
|
|
||
|
|
def __init__(self, client):
|
||
|
|
super(VirtualMachinePool, self).__init__('VM_POOL', 'VM', client)
|
||
|
|
|
||
|
|
def _factory(self, xml):
|
||
|
|
vm = VirtualMachine(xml, self.client)
|
||
|
|
vm._convert_types()
|
||
|
|
return vm
|
||
|
|
|
||
|
|
def info(self, filter=-3, range_start=-1, range_end=-1, vm_state=-1):
|
||
|
|
"""
|
||
|
|
Retrives/Refreshes virtual machine pool information
|
||
|
|
|
||
|
|
``filter``
|
||
|
|
Filter flag. By defaults retrives only connected user reources.
|
||
|
|
|
||
|
|
``range_start``
|
||
|
|
Range start ID. -1 for all
|
||
|
|
|
||
|
|
``range_end``
|
||
|
|
Range end ID. -1 for all
|
||
|
|
|
||
|
|
``vm_state``
|
||
|
|
|
||
|
|
VM state to filter by.
|
||
|
|
|
||
|
|
* \-2 Any state, including DONE
|
||
|
|
* \-1 Any state, except DONE (Defualt)
|
||
|
|
* 0 INIT
|
||
|
|
* 1 PENDING
|
||
|
|
* 2 HOLD
|
||
|
|
* 3 ACTIVE
|
||
|
|
* 4 STOPPED
|
||
|
|
* 5 SUSPENDED
|
||
|
|
* 6 DONE
|
||
|
|
* 7 FAILED
|
||
|
|
* 8 POWEROFF
|
||
|
|
* 9 UNDEPLYED
|
||
|
|
|
||
|
|
"""
|
||
|
|
super(VirtualMachinePool, self).info(filter, range_start,
|
||
|
|
range_end, vm_state)
|
||
|
|
|
||
|
|
def infoextended(self, filter=-3, range_start=-1, range_end=-1,
|
||
|
|
vm_state =-2, filter_key_value_str=''):
|
||
|
|
"""
|
||
|
|
Retrives/Refreshes extended resource pool information
|
||
|
|
|
||
|
|
Note: Opennebula 5.8 curtailed the amount of info that can be obtained
|
||
|
|
via `one.vmpool.info`. The detailed info of a VM is now available via
|
||
|
|
`one.vmpool.infoextended` xml-rpc call.
|
||
|
|
|
||
|
|
http://docs.opennebula.org/5.8/intro_release_notes/release_notes/compatibility.html#xmlrpc-api-changes
|
||
|
|
https://github.com/OpenNebula/one/issues/3076
|
||
|
|
|
||
|
|
``filter``
|
||
|
|
Filter flag. By defaults retrieves only connected user resources.
|
||
|
|
|
||
|
|
``range_start``
|
||
|
|
Range start ID. -1 for all
|
||
|
|
|
||
|
|
``range_end``
|
||
|
|
Range end ID. -1 for all
|
||
|
|
|
||
|
|
``filter_key_value_str``
|
||
|
|
String that needs to be searched for in the vmpool. Default search
|
||
|
|
everything
|
||
|
|
e.g.: ID=3023
|
||
|
|
"""
|
||
|
|
self[:] = []
|
||
|
|
data = self.client.call(
|
||
|
|
self.METHODS['infoextended'], filter, range_start, range_end,
|
||
|
|
vm_state, filter_key_value_str
|
||
|
|
)
|
||
|
|
self._initialize_xml(data, self.pool_name)
|
||
|
|
for element in self.xml.findall(self.element_name):
|
||
|
|
self.append(self._factory(element))
|