os-xenapi/os_xenapi/dom0/etc/xapi.d/plugins/dom0_pluginlib.py

151 lines
5.0 KiB
Python

# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# NOTE: XenServer still only supports Python 2.4 in it's dom0 userspace
# which means the Nova xenapi plugins must use only Python 2.4 features
#
# Helper functions for the Nova xapi plugins. In time, this will merge
# with the pluginlib.py shipped with xapi, but for now, that file is not
# very stable, so it's easiest just to have a copy of all the functions
# that we need.
#
import logging
import logging.handlers
import os
import time
import XenAPI
# global variable definition
MAX_VBD_UNPLUG_RETRIES = 30
# Logging setup
def configure_logging(name):
log = logging.getLogger()
log.setLevel(logging.DEBUG)
if os.path.exists('/dev/log'):
sysh = logging.handlers.SysLogHandler('/dev/log')
sysh.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%s: %%(levelname)-8s%%(message)s' % name)
sysh.setFormatter(formatter)
log.addHandler(sysh)
# Exceptions
class PluginError(Exception):
"""Base Exception class for all plugin errors."""
def __init__(self, *args):
Exception.__init__(self, *args)
class ArgumentError(PluginError):
# Raised when required arguments are missing, argument values are invalid,
# or incompatible arguments are given.
def __init__(self, *args):
PluginError.__init__(self, *args)
# Argument validation
def exists(args, key):
# Validates that a freeform string argument to a RPC method call is given.
# Returns the string.
if key in args:
return args[key]
else:
raise ArgumentError('Argument %s is required.' % key)
def optional(args, key):
# If the given key is in args, return the corresponding value, otherwise
# return None
return key in args and args[key] or None
def _get_domain_0(session):
this_host_ref = session.xenapi.session.get_this_host(session.handle)
expr = 'field "is_control_domain" = "true" and field "resident_on" = "%s"'
expr = expr % this_host_ref
return list(session.xenapi.VM.get_all_records_where(expr).keys())[0]
def with_vdi_in_dom0(session, vdi, read_only, f):
dom0 = _get_domain_0(session)
vbd_rec = {}
vbd_rec['VM'] = dom0
vbd_rec['VDI'] = vdi
vbd_rec['userdevice'] = 'autodetect'
vbd_rec['bootable'] = False
vbd_rec['mode'] = read_only and 'RO' or 'RW'
vbd_rec['type'] = 'disk'
vbd_rec['unpluggable'] = True
vbd_rec['empty'] = False
vbd_rec['other_config'] = {}
vbd_rec['qos_algorithm_type'] = ''
vbd_rec['qos_algorithm_params'] = {}
vbd_rec['qos_supported_algorithms'] = []
logging.debug('Creating VBD for VDI %s ... ', vdi)
vbd = session.xenapi.VBD.create(vbd_rec)
logging.debug('Creating VBD for VDI %s done.', vdi)
try:
logging.debug('Plugging VBD %s ... ', vbd)
session.xenapi.VBD.plug(vbd)
logging.debug('Plugging VBD %s done.', vbd)
return f(session.xenapi.VBD.get_device(vbd))
finally:
logging.debug('Destroying VBD for VDI %s ... ', vdi)
_vbd_unplug_with_retry(session, vbd)
try:
session.xenapi.VBD.destroy(vbd)
except XenAPI.Failure as e: # noqa
logging.error('Ignoring XenAPI.Failure %s', e)
logging.debug('Destroying VBD for VDI %s done.', vdi)
def _vbd_unplug_with_retry(session, vbd):
"""Call VBD.unplug on the given VBD
with a retry if we get DEVICE_DETACH_REJECTED. For reasons which I don't
understand, we're seeing the device still in use, even when all processes
using the device should be dead.
"""
retry_count = MAX_VBD_UNPLUG_RETRIES
while True:
try:
session.xenapi.VBD.unplug(vbd)
logging.debug('VBD.unplug successful first time.')
return
except XenAPI.Failure as e: # noqa
if (len(e.details) > 0 and
e.details[0] == 'DEVICE_DETACH_REJECTED'):
retry_count -= 1
if (retry_count <= 0):
raise PluginError('VBD.unplug failed after retry %s times.'
% MAX_VBD_UNPLUG_RETRIES)
logging.debug('VBD.unplug rejected: retrying...')
time.sleep(1)
elif (len(e.details) > 0 and
e.details[0] == 'DEVICE_ALREADY_DETACHED'):
logging.debug('VBD.unplug successful eventually.')
return
else:
logging.error('Ignoring XenAPI.Failure in VBD.unplug: %s', e)
return