~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-01-21 11:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20110121114806-v8fvnnl6az4m4ohv
Tags: upstream-2011.1~bzr597
ImportĀ upstreamĀ versionĀ 2011.1~bzr597

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2010 Citrix Systems, Inc.
2
 
#
3
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
 
#    not use this file except in compliance with the License. You may obtain
5
 
#    a copy of the License at
6
 
#
7
 
#         http://www.apache.org/licenses/LICENSE-2.0
8
 
#
9
 
#    Unless required by applicable law or agreed to in writing, software
10
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
 
#    License for the specific language governing permissions and limitations
13
 
#    under the License.
14
 
 
15
 
#
16
 
# Helper functions for the Nova xapi plugins.  In time, this will merge
17
 
# with the pluginlib.py shipped with xapi, but for now, that file is not
18
 
# very stable, so it's easiest just to have a copy of all the functions
19
 
# that we need.
20
 
#
21
 
 
22
 
import httplib
23
 
import logging
24
 
import logging.handlers
25
 
import re
26
 
import time
27
 
 
28
 
 
29
 
##### Logging setup
30
 
 
31
 
def configure_logging(name):
32
 
    log = logging.getLogger()
33
 
    log.setLevel(logging.DEBUG)
34
 
    sysh = logging.handlers.SysLogHandler('/dev/log')
35
 
    sysh.setLevel(logging.DEBUG)
36
 
    formatter = logging.Formatter('%s: %%(levelname)-8s %%(message)s' % name)
37
 
    sysh.setFormatter(formatter)
38
 
    log.addHandler(sysh)
39
 
 
40
 
 
41
 
##### Exceptions
42
 
 
43
 
class PluginError(Exception):
44
 
    """Base Exception class for all plugin errors."""
45
 
    def __init__(self, *args):
46
 
        Exception.__init__(self, *args)
47
 
 
48
 
class ArgumentError(PluginError):
49
 
    """Raised when required arguments are missing, argument values are invalid,
50
 
    or incompatible arguments are given.
51
 
    """
52
 
    def __init__(self, *args):
53
 
        PluginError.__init__(self, *args)
54
 
 
55
 
 
56
 
##### Helpers
57
 
 
58
 
def ignore_failure(func, *args, **kwargs):
59
 
    try:
60
 
        return func(*args, **kwargs)
61
 
    except XenAPI.Failure, e:
62
 
        logging.error('Ignoring XenAPI.Failure %s', e)
63
 
        return None
64
 
 
65
 
 
66
 
##### Argument validation
67
 
 
68
 
ARGUMENT_PATTERN = re.compile(r'^[a-zA-Z0-9_:\.\-,]+$')
69
 
 
70
 
def validate_exists(args, key, default=None):
71
 
    """Validates that a string argument to a RPC method call is given, and
72
 
    matches the shell-safe regex, with an optional default value in case it
73
 
    does not exist.
74
 
 
75
 
    Returns the string.
76
 
    """
77
 
    if key in args:
78
 
        if len(args[key]) == 0:
79
 
            raise ArgumentError('Argument %r value %r is too short.' % (key, args[key]))
80
 
        if not ARGUMENT_PATTERN.match(args[key]):
81
 
            raise ArgumentError('Argument %r value %r contains invalid characters.' % (key, args[key]))
82
 
        if args[key][0] == '-':
83
 
            raise ArgumentError('Argument %r value %r starts with a hyphen.' % (key, args[key]))
84
 
        return args[key]
85
 
    elif default is not None:
86
 
        return default
87
 
    else:
88
 
        raise ArgumentError('Argument %s is required.' % key)
89
 
 
90
 
def validate_bool(args, key, default=None):
91
 
    """Validates that a string argument to a RPC method call is a boolean string,
92
 
    with an optional default value in case it does not exist.
93
 
 
94
 
    Returns the python boolean value.
95
 
    """
96
 
    value = validate_exists(args, key, default)
97
 
    if value.lower() == 'true':
98
 
        return True
99
 
    elif value.lower() == 'false':
100
 
        return False
101
 
    else:
102
 
        raise ArgumentError("Argument %s may not take value %r. Valid values are ['true', 'false']." % (key, value))
103
 
 
104
 
def exists(args, key):
105
 
    """Validates that a freeform string argument to a RPC method call is given.
106
 
    Returns the string.
107
 
    """
108
 
    if key in args:
109
 
        return args[key]
110
 
    else:
111
 
        raise ArgumentError('Argument %s is required.' % key)
112
 
 
113
 
def optional(args, key):
114
 
    """If the given key is in args, return the corresponding value, otherwise
115
 
    return None"""
116
 
    return key in args and args[key] or None
117
 
 
118
 
 
119
 
def get_this_host(session):
120
 
    return session.xenapi.session.get_this_host(session.handle)
121
 
 
122
 
 
123
 
def get_domain_0(session):
124
 
    this_host_ref = get_this_host(session)
125
 
    expr = 'field "is_control_domain" = "true" and field "resident_on" = "%s"' % this_host_ref
126
 
    return session.xenapi.VM.get_all_records_where(expr).keys()[0]
127
 
 
128
 
 
129
 
def create_vdi(session, sr_ref, name_label, virtual_size, read_only):
130
 
    vdi_ref = session.xenapi.VDI.create(
131
 
        { 'name_label': name_label,
132
 
          'name_description': '',
133
 
          'SR': sr_ref,
134
 
          'virtual_size': str(virtual_size),
135
 
          'type': 'User',
136
 
          'sharable': False,
137
 
          'read_only': read_only,
138
 
          'xenstore_data': {},
139
 
          'other_config': {},
140
 
          'sm_config': {},
141
 
          'tags': [] })
142
 
    logging.debug('Created VDI %s (%s, %s, %s) on %s.', vdi_ref, name_label,
143
 
                  virtual_size, read_only, sr_ref)
144
 
    return vdi_ref
145
 
 
146
 
 
147
 
def with_vdi_in_dom0(session, vdi, read_only, f):
148
 
    dom0 = get_domain_0(session)
149
 
    vbd_rec = {}
150
 
    vbd_rec['VM'] = dom0
151
 
    vbd_rec['VDI'] = vdi
152
 
    vbd_rec['userdevice'] = 'autodetect'
153
 
    vbd_rec['bootable'] = False
154
 
    vbd_rec['mode'] = read_only and 'RO' or 'RW'
155
 
    vbd_rec['type'] = 'disk'
156
 
    vbd_rec['unpluggable'] = True
157
 
    vbd_rec['empty'] = False
158
 
    vbd_rec['other_config'] = {}
159
 
    vbd_rec['qos_algorithm_type'] = ''
160
 
    vbd_rec['qos_algorithm_params'] = {}
161
 
    vbd_rec['qos_supported_algorithms'] = []
162
 
    logging.debug('Creating VBD for VDI %s ... ', vdi)
163
 
    vbd = session.xenapi.VBD.create(vbd_rec)
164
 
    logging.debug('Creating VBD for VDI %s done.', vdi)
165
 
    try:
166
 
        logging.debug('Plugging VBD %s ... ', vbd)
167
 
        session.xenapi.VBD.plug(vbd)
168
 
        logging.debug('Plugging VBD %s done.', vbd)
169
 
        return f(session.xenapi.VBD.get_device(vbd))
170
 
    finally:
171
 
        logging.debug('Destroying VBD for VDI %s ... ', vdi)
172
 
        vbd_unplug_with_retry(session, vbd)
173
 
        ignore_failure(session.xenapi.VBD.destroy, vbd)
174
 
        logging.debug('Destroying VBD for VDI %s done.', vdi)
175
 
 
176
 
 
177
 
def vbd_unplug_with_retry(session, vbd):
178
 
    """Call VBD.unplug on the given VBD, with a retry if we get
179
 
    DEVICE_DETACH_REJECTED.  For reasons which I don't understand, we're
180
 
    seeing the device still in use, even when all processes using the device
181
 
    should be dead."""
182
 
    while True:
183
 
        try:
184
 
            session.xenapi.VBD.unplug(vbd)
185
 
            logging.debug('VBD.unplug successful first time.')
186
 
            return
187
 
        except XenAPI.Failure, e:
188
 
            if (len(e.details) > 0 and
189
 
                e.details[0] == 'DEVICE_DETACH_REJECTED'):
190
 
                logging.debug('VBD.unplug rejected: retrying...')
191
 
                time.sleep(1)
192
 
            elif (len(e.details) > 0 and
193
 
                  e.details[0] == 'DEVICE_ALREADY_DETACHED'):
194
 
                logging.debug('VBD.unplug successful eventually.')
195
 
                return
196
 
            else:
197
 
                logging.error('Ignoring XenAPI.Failure in VBD.unplug: %s', e)
198
 
                return
199
 
 
200
 
 
201
 
def with_http_connection(proto, netloc, f):
202
 
    conn = (proto == 'https' and
203
 
            httplib.HTTPSConnection(netloc) or
204
 
            httplib.HTTPConnection(netloc))
205
 
    try:
206
 
        return f(conn)
207
 
    finally:
208
 
        conn.close()
209
 
 
210
 
 
211
 
def with_file(dest_path, mode, f):
212
 
    dest = open(dest_path, mode)
213
 
    try:
214
 
        return f(dest)
215
 
    finally:
216
 
        dest.close()