ia64/xen-unstable

changeset 12124:91c7ee18c978

[XEND] Updates to SR and VDI implementations

* Moved xenapi transport util method, stringify to xen.util.xmlrpclib2
* XenVDI now preserves configuration to an XML-ish file
* Update Xen API's class names to be all lowercase
* Update get_by_label to get_by_name_label and return sets as the API
expects.
* Add support for VBD creation with a VDI reference.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Oct 13 15:13:21 2006 +0100 (2006-10-13)
parents 58521d4b7c7b
children 0a8b854697ad
files tools/python/scripts/xapi.py tools/python/scripts/xapi.vdicfg.py tools/python/xen/util/xmlrpclib2.py tools/python/xen/xend/XendAPI.py tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/XendStorageRepository.py tools/python/xen/xend/XendVDI.py
line diff
     1.1 --- a/tools/python/scripts/xapi.py	Thu Oct 12 18:51:17 2006 +0100
     1.2 +++ b/tools/python/scripts/xapi.py	Fri Oct 13 15:13:21 2006 +0100
     1.3 @@ -21,15 +21,23 @@ from optparse import *
     1.4  from pprint import pprint
     1.5  from types import DictType
     1.6  
     1.7 +MB = 1024 * 1024
     1.8 +
     1.9  HOST_INFO_FORMAT = '%-20s: %-50s'
    1.10  VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(vcpus_number)-5s'\
    1.11 -                 ' %(power_state)-12s %(uuid)-32s'
    1.12 -
    1.13 +                 ' %(power_state)-12s %(uuid)-36s'
    1.14 +SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \
    1.15 +                 '%(type)-10s'
    1.16 +VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\
    1.17 +                  '%(sector_size)-8s'
    1.18  LOGIN = ('atse', 'passwd')
    1.19  
    1.20  COMMANDS = {
    1.21      'host-info': ('', 'Get Xen Host Info'),
    1.22 +    'sr-list':   ('', 'List all SRs'),
    1.23      'vbd-create': ('<domname> <pycfg>', 'Create VBD attached to domname'),
    1.24 +    'vdi-list'  : ('', 'List all VDI'),
    1.25 +    'vdi-delete': ('<vdi_uuid>', 'Delete VDI'),
    1.26      'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'),
    1.27  
    1.28      'vm-create': ('<pycfg>', 'Create VM with python config'),
    1.29 @@ -84,8 +92,8 @@ def execute(fn, *args):
    1.30  
    1.31  def _connect(*args):
    1.32      server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock')        
    1.33 -    session = execute(server.Session.login_with_password, *LOGIN)
    1.34 -    host = execute(server.Session.get_this_host, session)
    1.35 +    session = execute(server.session.login_with_password, *LOGIN)
    1.36 +    host = execute(server.session.get_this_host, session)
    1.37      return (server, session)
    1.38  
    1.39  def _stringify(adict):
    1.40 @@ -248,8 +256,55 @@ def xapi_vif_create(*args):
    1.41      vif_uuid = execute(server.VIF.create, session, cfg)
    1.42      print 'Done. (%s)' % vif_uuid
    1.43  
    1.44 +def xapi_vdi_list(*args):
    1.45 +    server, session = _connect()
    1.46 +    vdis = execute(server.VDI.get_all, session)
    1.47 +
    1.48 +    print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
    1.49 +                             'uuid' : 'UUID',
    1.50 +                             'virtual_size': 'Sectors',
    1.51 +                             'sector_size': 'Sector Size'}
    1.52      
    1.53 +    for vdi in vdis:
    1.54 +        vdi_struct = execute(server.VDI.get_record, session, vdi)
    1.55 +        print VDI_LIST_FORMAT % vdi_struct
    1.56  
    1.57 +def xapi_sr_list(*args):
    1.58 +    server, session = _connect()
    1.59 +    srs = execute(server.SR.get_all, session)
    1.60 +    print SR_LIST_FORMAT % {'name_label': 'SR Label',
    1.61 +                            'uuid' : 'UUID',
    1.62 +                            'physical_size': 'Size',
    1.63 +                            'type': 'Type'}
    1.64 +    for sr in srs:
    1.65 +        sr_struct = execute(server.SR.get_record, session, sr)
    1.66 +        sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB
    1.67 +        print SR_LIST_FORMAT % sr_struct
    1.68 +
    1.69 +def xapi_vdi_create(*args):
    1.70 +    server, session = _connect()
    1.71 +    cfg = _read_python_cfg(args[0])
    1.72 +
    1.73 +    srs = execute(server.SR.get_all, session)
    1.74 +    sr = srs[0]
    1.75 +    cfg['SR'] = sr
    1.76 +
    1.77 +    size = (cfg['virtual_size'] * cfg['sector_size'])/MB
    1.78 +    print 'Creating VDI of size: %dMB' % size
    1.79 +    uuid = execute(server.VDI.create, session, cfg)
    1.80 +    print 'Done. (%s)' % uuid
    1.81 +
    1.82 +def xapi_vdi_delete(*args):
    1.83 +    server, session = _connect()
    1.84 +    if len(args) < 1:
    1.85 +        raise OptionError('Not enough arguments')
    1.86 +
    1.87 +    vdi_uuid = args[0]
    1.88 +    print 'Deleting VDI %s' % vdi_uuid
    1.89 +    result = execute(server.VDI.destroy, session, vdi_uuid)
    1.90 +    print 'Done.'
    1.91 +    
    1.92 +        
    1.93  #
    1.94  # Command Line Utils
    1.95  #
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/python/scripts/xapi.vdicfg.py	Fri Oct 13 15:13:21 2006 +0100
     2.3 @@ -0,0 +1,7 @@
     2.4 +name_label = 'VDI 1'
     2.5 +name_description = ''
     2.6 +virtual_size = 10 * 1024
     2.7 +sector_size = 1024
     2.8 +type = 'system'
     2.9 +sharable = False
    2.10 +read_only = False
     3.1 --- a/tools/python/xen/util/xmlrpclib2.py	Thu Oct 12 18:51:17 2006 +0100
     3.2 +++ b/tools/python/xen/util/xmlrpclib2.py	Fri Oct 13 15:13:21 2006 +0100
     3.3 @@ -21,8 +21,9 @@ An enhanced XML-RPC client/server interf
     3.4  """
     3.5  
     3.6  import string
     3.7 -import types
     3.8  import fcntl
     3.9 +from types import *
    3.10 +    
    3.11  
    3.12  from httplib import HTTPConnection, HTTP
    3.13  from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
    3.14 @@ -39,6 +40,23 @@ except ImportError:
    3.15      # package.
    3.16      ssh_enabled = False
    3.17  
    3.18 +#
    3.19 +# Convert all integers to strings as described in the Xen API
    3.20 +#
    3.21 +
    3.22 +
    3.23 +def stringify(value):
    3.24 +    if isinstance(value, IntType) and not isinstance(value, BooleanType):
    3.25 +        return str(value)
    3.26 +    elif isinstance(value, DictType):
    3.27 +        for k, v in value.items():
    3.28 +            value[k] = stringify(v)
    3.29 +        return value
    3.30 +    elif isinstance(value, (TupleType, ListType)):
    3.31 +        return [stringify(v) for v in value]
    3.32 +    else:
    3.33 +        return value
    3.34 +
    3.35  
    3.36  # A new ServerProxy that also supports httpu urls.  An http URL comes in the
    3.37  # form:
    3.38 @@ -91,8 +109,7 @@ class UnixTransport(xmlrpclib.Transport)
    3.39  
    3.40  # See _marshalled_dispatch below.
    3.41  def conv_string(x):
    3.42 -    if (isinstance(x, types.StringType) or
    3.43 -        isinstance(x, unicode)):
    3.44 +    if isinstance(x, StringTypes):
    3.45          s = string.replace(x, "'", r"\047")
    3.46          exec "s = '" + s + "'"
    3.47          return s
    3.48 @@ -169,8 +186,7 @@ class TCPXMLRPCServer(SocketServer.Threa
    3.49              # to transmit the string using Python encoding.
    3.50              # Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried
    3.51              # in xml_pickle.py).
    3.52 -            if (isinstance(response, types.StringType) or
    3.53 -                isinstance(response, unicode)):
    3.54 +            if isinstance(response, StringTypes):
    3.55                  response = repr(response)[1:-1]
    3.56  
    3.57              response = (response,)
     4.1 --- a/tools/python/xen/xend/XendAPI.py	Thu Oct 12 18:51:17 2006 +0100
     4.2 +++ b/tools/python/xen/xend/XendAPI.py	Fri Oct 13 15:13:21 2006 +0100
     4.3 @@ -25,23 +25,10 @@ from xen.xend.XendClient import ERROR_IN
     4.4  from xen.xend.XendLogging import log
     4.5  
     4.6  from xen.xend.XendAPIConstants import *
     4.7 -
     4.8 -from types import *
     4.9 +from xen.util.xmlrpclib2 import stringify
    4.10  
    4.11 -def _stringify(value):
    4.12 -    if isinstance(value, IntType) and not isinstance(value, BooleanType):
    4.13 -        return str(value)
    4.14 -    elif isinstance(value, DictType):
    4.15 -        for k, v in value.items():
    4.16 -            value[k] = _stringify(v)
    4.17 -        return value
    4.18 -    elif isinstance(value, (TupleType, ListType)):
    4.19 -        return [_stringify(v) for v in value]
    4.20 -    else:
    4.21 -        return value
    4.22 -    
    4.23  def xen_api_success(value):
    4.24 -    return {"Status": "Success", "Value": _stringify(value)}
    4.25 +    return {"Status": "Success", "Value": stringify(value)}
    4.26  
    4.27  def xen_api_success_void():
    4.28      """Return success, but caller expects no return value."""
    4.29 @@ -252,7 +239,7 @@ class XendAPI:
    4.30          """
    4.31          
    4.32          classes = {
    4.33 -            'Session': (session_required,),
    4.34 +            'session': (session_required,),
    4.35              'host': (valid_host, session_required),
    4.36              'host_cpu': (valid_host_cpu, session_required),
    4.37              'VM': (valid_vm, session_required),
    4.38 @@ -346,9 +333,9 @@ class XendAPI:
    4.39      # ----------------------------------------------------------------
    4.40      # NOTE: Left unwrapped by __init__
    4.41  
    4.42 -    Session_attr_ro = ['this_host', 'this_user']
    4.43 -    Session_methods = ['logout']
    4.44 -    # Session_funcs = ['login_with_password']    
    4.45 +    session_attr_ro = ['this_host', 'this_user']
    4.46 +    session_methods = ['logout']
    4.47 +    # session_funcs = ['login_with_password']    
    4.48  
    4.49      def session_login_with_password(self, username, password):
    4.50          try:
    4.51 @@ -356,7 +343,7 @@ class XendAPI:
    4.52              return xen_api_success(session)
    4.53          except XendError, e:
    4.54              return xen_api_error(XEND_ERROR_AUTHENTICATION_FAILED)
    4.55 -    session_login_with_password.api = 'Session.login_with_password'
    4.56 +    session_login_with_password.api = 'session.login_with_password'
    4.57  
    4.58  
    4.59      # object methods
    4.60 @@ -405,7 +392,7 @@ class XendAPI:
    4.61                      'reboot',
    4.62                      'shutdown']
    4.63      
    4.64 -    host_funcs = ['get_by_label']
    4.65 +    host_funcs = ['get_by_name_label']
    4.66  
    4.67      # attributes
    4.68      def host_get_name_label(self, session, host_ref):
    4.69 @@ -572,7 +559,7 @@ class XendAPI:
    4.70                    'suspend',
    4.71                    'resume']
    4.72      
    4.73 -    VM_funcs  = ['get_by_label']
    4.74 +    VM_funcs  = ['get_by_name_label']
    4.75  
    4.76      # parameters required for _create()
    4.77      VM_attr_inst = [
    4.78 @@ -892,7 +879,8 @@ class XendAPI:
    4.79      def vm_get_all(self, session):
    4.80          refs = [d.get_uuid() for d in XendDomain.instance().list()]
    4.81          return xen_api_success(refs)
    4.82 -    def vm_get_by_label(self, session, label):
    4.83 +    
    4.84 +    def vm_get_by_name_label(self, session, label):
    4.85          xendom = XendDomain.instance()
    4.86          dom = xendom.domain_lookup_nr(label)
    4.87          if dom:
    4.88 @@ -1022,16 +1010,27 @@ class XendAPI:
    4.89      # class methods
    4.90      def vbd_create(self, session, vbd_struct):
    4.91          xendom = XendDomain.instance()
    4.92 -        if xendom.is_valid_vm(vbd_struct['VM']):
    4.93 -            dom = xendom.get_vm_by_uuid(vbd_struct['VM'])
    4.94 -            try:
    4.95 +        if not xendom.is_valid_vm(vbd_struct['VM']):
    4.96 +            return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
    4.97 +        
    4.98 +        dom = xendom.get_vm_by_uuid(vbd_struct['VM'])
    4.99 +        vbd_ref = ''
   4.100 +        try:
   4.101 +            if vbd_struct.get('VDI', None):
   4.102 +                # this is a traditional VBD without VDI and SR 
   4.103                  vbd_ref = dom.create_vbd(vbd_struct)
   4.104 -                xendom.managed_config_save(dom)
   4.105 -                return xen_api_success(vbd_ref)
   4.106 -            except XendError:
   4.107 -                return xen_api_error(XEND_ERROR_TODO)
   4.108 -        else:
   4.109 -            return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
   4.110 +            else:
   4.111 +                # new VBD via VDI/SR
   4.112 +                vdi_ref = vbd_struct.get('VDI')
   4.113 +                sr = XendNode.instance().get_sr()
   4.114 +                vdi_image = sr.xen_api_get_by_uuid(vdi_ref)
   4.115 +                vdi_image_path = vdi_image.image_path
   4.116 +                vbd_ref = dom.create_vbd_with_vdi(vbd_struct, vdi_image_path)
   4.117 +        except XendError:
   4.118 +            return xen_api_todo()
   4.119 +
   4.120 +        xendom.managed_config_save(dom)
   4.121 +        return xen_api_success(vbd_ref)
   4.122  
   4.123      # attributes (rw)
   4.124      def vbd_get_vm(self, session, vbd_ref):
   4.125 @@ -1118,61 +1117,144 @@ class XendAPI:
   4.126      VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
   4.127  
   4.128      VDI_methods = ['snapshot']
   4.129 -    VDI_funcs = ['get_by_label']
   4.130 +    VDI_funcs = ['get_by_name_label']
   4.131 +    
   4.132      def vdi_get_vbds(self, session, vdi_ref):
   4.133          return xen_api_todo()
   4.134 +    
   4.135      def vdi_get_physical_utilisation(self, session, vdi_ref):
   4.136 -        return xen_api_todo()
   4.137 +        sr = XendNode.instance().get_sr()
   4.138 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.139 +        return xen_api_success(image.get_physical_utilisation())        
   4.140 +    
   4.141      def vdi_get_sector_size(self, session, vdi_ref):
   4.142 -        return xen_api_todo()
   4.143 +        sr = XendNode.instance().get_sr()
   4.144 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.145 +        return xen_api_success(image.sector_size)        
   4.146 +    
   4.147      def vdi_get_type(self, session, vdi_ref):
   4.148 -        return xen_api_todo()
   4.149 +        sr = XendNode.instance().get_sr()
   4.150 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.151 +        return xen_api_success(image.type)
   4.152 +    
   4.153      def vdi_get_parent(self, session, vdi_ref):
   4.154 -        return xen_api_todo()
   4.155 +        sr = XendNode.instance().get_sr()
   4.156 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.157 +        return xen_api_success(image.parent)        
   4.158 +    
   4.159      def vdi_get_children(self, session, vdi_ref):
   4.160 -        return xen_api_todo()
   4.161 +        sr = XendNode.instance().get_sr()
   4.162 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.163 +        return xen_api_success(image.children)        
   4.164 +    
   4.165      def vdi_get_name_label(self, session, vdi_ref):
   4.166 -        return xen_api_todo()
   4.167 +        sr = XendNode.instance().get_sr()
   4.168 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.169 +        return xen_api_success(image.name_label)
   4.170 +
   4.171      def vdi_get_name_description(self, session, vdi_ref):
   4.172 -        return xen_api_todo()
   4.173 +        sr = XendNode.instance().get_sr()
   4.174 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.175 +        return xen_api_success(image.name_description)
   4.176 +
   4.177      def vdi_get_sr(self, session, vdi_ref):
   4.178 -        return xen_api_todo()
   4.179 +        sr = XendNode.instance().get_sr()
   4.180 +        return xen_api_success(sr.uuid)
   4.181 +
   4.182      def vdi_get_virtual_size(self, session, vdi_ref):
   4.183 -        return xen_api_todo()
   4.184 +        sr = XendNode.instance().get_sr()
   4.185 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.186 +        return xen_api_success(image.virtual_size)
   4.187 +
   4.188      def vdi_get_sharable(self, session, vdi_ref):
   4.189 -        return xen_api_todo()
   4.190 +        sr = XendNode.instance().get_sr()
   4.191 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.192 +        return xen_api_success(image.sharable)
   4.193 +
   4.194      def vdi_get_read_only(self, session, vdi_ref):
   4.195 -        return xen_api_todo()
   4.196 -    def vdi_get_uuid(self, session, vdi_ref):
   4.197 -        return xen_api_todo()
   4.198 +        sr = XendNode.instance().get_sr()
   4.199 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.200 +        return xen_api_success(image.sharable)        
   4.201 +
   4.202      def vdi_set_name_label(self, session, vdi_ref, value):
   4.203 -        return xen_api_todo()
   4.204 +        sr = XendNode.instance().get_sr()
   4.205 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.206 +        image.name_label = value
   4.207 +        return xen_api_success_void()
   4.208 +
   4.209      def vdi_set_name_description(self, session, vdi_ref, value):
   4.210 -        return xen_api_todo()
   4.211 +        sr = XendNode.instance().get_sr()
   4.212 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.213 +        image.name_description = value
   4.214 +        return xen_api_success_void()
   4.215 +
   4.216      def vdi_set_sr(self, session, vdi_ref, value):
   4.217 -        return xen_api_todo()
   4.218 +        return xen_api_error(XEND_ERROR_UNSUPPORTED)
   4.219 +
   4.220      def vdi_set_virtual_size(self, session, vdi_ref, value):
   4.221 -        return xen_api_todo()
   4.222 +        return xen_api_error(XEND_ERROR_UNSUPPORTED)
   4.223 +
   4.224      def vdi_set_sharable(self, session, vdi_ref, value):
   4.225          return xen_api_todo()
   4.226      def vdi_set_read_only(self, session, vdi_ref, value):
   4.227          return xen_api_todo()
   4.228 +
   4.229 +    # Object Methods
   4.230      def vdi_snapshot(self, session, vdi_ref):
   4.231          return xen_api_todo()
   4.232 +    
   4.233      def vdi_destroy(self, session, vdi_ref):
   4.234 -        return xen_api_todo()
   4.235 +        sr = XendNode.instance().get_sr()
   4.236 +        sr.destroy_image(vdi_ref)
   4.237 +        return xen_api_success_void()
   4.238 +
   4.239      def vdi_to_xml(self, session, vdi_ref):
   4.240          return xen_api_todo()
   4.241 +    
   4.242      def vdi_get_record(self, session, vdi_ref):
   4.243 -        return xen_api_todo()
   4.244 -    def vdi_create(self, session):
   4.245 -        return xen_api_todo()
   4.246 -    def vdi_get_by_uuid(self, session):
   4.247 -        return xen_api_todo()
   4.248 +        sr = XendNode.instance().get_sr()
   4.249 +        image = sr.xen_api_get_by_uuid(vdi_ref)
   4.250 +        if image:
   4.251 +            return xen_api_success({
   4.252 +                'uuid': vdi_ref,
   4.253 +                'name_label': image.name_label,
   4.254 +                'name_description': image.name_description,
   4.255 +                'SR': sr.uuid,
   4.256 +                'VBDs': [], # TODO
   4.257 +                'virtual_size': image.virtual_size,
   4.258 +                'physical_utilisation': image.physical_utilisation,
   4.259 +                'sector_size': image.sector_size,
   4.260 +                'type': image.type,
   4.261 +                'parent': image.parent,
   4.262 +                'children': image.children,
   4.263 +                'sharable': image.sharable,
   4.264 +                'read_only': image.read_only,
   4.265 +                })
   4.266 +
   4.267 +        return xen_api_error(XEND_ERROR_VDI_INVALID)
   4.268 +
   4.269 +    # Class Functions    
   4.270 +    def vdi_create(self, session, vdi_struct):
   4.271 +        sr = XendNode.instance().get_sr()
   4.272 +        sr_ref = vdi_struct['SR']
   4.273 +        if sr.uuid != sr_ref:
   4.274 +            return xen_api_error(XEND_ERROR_SR_INVALID)
   4.275 +
   4.276 +        vdi_uuid = sr.create_image(vdi_struct)
   4.277 +        return xen_api_success(vdi_uuid)
   4.278 +
   4.279      def vdi_get_all(self, session):
   4.280 -        return xen_api_todo()
   4.281 -    def vdi_get_by_label(self, session):
   4.282 -        return xen_api_todo()
   4.283 +        sr = XendNode.instance().get_sr()
   4.284 +        return xen_api_success(sr.list_images())
   4.285 +    
   4.286 +    def vdi_get_by_name_label(self, session, name):
   4.287 +        sr = XendNode.instance().get_sr()
   4.288 +        image_uuid = sr.xen_api_get_by_name_label(name)
   4.289 +        if image_uuid:
   4.290 +            return xen_api_success(image_uuid)
   4.291 +        
   4.292 +        return xen_api_error(XEND_ERROR_VDI_INVALID)
   4.293 +
   4.294  
   4.295      # Xen API: Class SR
   4.296      # ----------------------------------------------------------------
   4.297 @@ -1193,14 +1275,14 @@ class XendAPI:
   4.298                      'name_description']
   4.299      
   4.300      SR_methods = ['clone']
   4.301 -    SR_funcs = ['get_by_label']
   4.302 +    SR_funcs = ['get_by_name_label']
   4.303  
   4.304      # Class Functions
   4.305      def sr_get_all(self, session):
   4.306          sr = XendNode.instance().get_sr()
   4.307          return xen_api_success([sr.uuid])
   4.308  
   4.309 -    def sr_get_by_label(self, session, label):
   4.310 +    def sr_get_by_name_label(self, session, label):
   4.311          sr = XendNode.instance().get_sr()
   4.312          if sr.name_label != label:
   4.313              return xen_api_error(XEND_ERROR_SR_INVALID)
     5.1 --- a/tools/python/xen/xend/XendConfig.py	Thu Oct 12 18:51:17 2006 +0100
     5.2 +++ b/tools/python/xen/xend/XendConfig.py	Fri Oct 13 15:13:21 2006 +0100
     5.3 @@ -566,7 +566,7 @@ class XendConfig(dict):
     5.4          for dev_uuid, (dev_type, dev_info) in cfg['device'].items():
     5.5              if dev_type == 'vif':
     5.6                  cfg['vif_refs'].append(dev_uuid)
     5.7 -            elif dev_type == 'vbd':
     5.8 +            elif dev_type in ('vbd','tap'):
     5.9                  cfg['vbd_refs'].append(dev_uuid)
    5.10                  
    5.11          return cfg
    5.12 @@ -771,6 +771,8 @@ class XendConfig(dict):
    5.13              self['device'][dev_uuid] = (dev_type, dev_info)
    5.14              if dev_type in ('vif', 'vbd'):
    5.15                  self['%s_refs' % dev_type].append(dev_uuid)
    5.16 +            elif dev_type in ('tap',):
    5.17 +                self['vbd_refs'].append(dev_uuid)
    5.18              return dev_uuid
    5.19  
    5.20          if cfg_xenapi:
    5.21 @@ -805,7 +807,21 @@ class XendConfig(dict):
    5.22                  self['device'][dev_uuid] = (dev_type, dev_info)
    5.23                  self['vbd_refs'].append(dev_uuid)                
    5.24                  return dev_uuid
    5.25 +            
    5.26 +            elif dev_type == 'tap':
    5.27 +                dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
    5.28 +                dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
    5.29                  
    5.30 +                if cfg_xenapi.get('mode') == 'RW':
    5.31 +                    dev_info['mode'] = 'w'
    5.32 +                else:
    5.33 +                    dev_info['mode'] = 'r'
    5.34 +
    5.35 +                dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
    5.36 +                dev_info['uuid'] = dev_uuid
    5.37 +                self['device'][dev_uuid] = (dev_type, dev_info)
    5.38 +                self['vbd_refs'].append(dev_uuid)                
    5.39 +                return dev_uuid                
    5.40                  
    5.41          return ''
    5.42  
     6.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Thu Oct 12 18:51:17 2006 +0100
     6.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Fri Oct 13 15:13:21 2006 +0100
     6.3 @@ -1826,8 +1826,33 @@ class XendDomainInfo:
     6.4  
     6.5          return dev_uuid
     6.6  
     6.7 +    def create_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
     6.8 +        """Create a VBD using a VDI from XendStorageRepository.
     6.9 +
    6.10 +        @param xenapi_vbd: vbd struct from the Xen API
    6.11 +        @param vdi_image_path: VDI UUID
    6.12 +        @rtype: string
    6.13 +        @return: uuid of the device
    6.14 +        """
    6.15 +        xenapi_vbd['image'] = vdi_image_path
    6.16 +        dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
    6.17 +        if not dev_uuid:
    6.18 +            raise XendError('Failed to create device')
    6.19 +
    6.20 +        if self.state in (XEN_API_VM_POWER_STATE_RUNNING,):
    6.21 +            sxpr = self.info.device_sxpr(dev_uuid)
    6.22 +            devid = self.getDeviceController('tap').createDevice(sxpr)
    6.23 +            raise XendError("Device creation failed")
    6.24 +
    6.25 +        return dev_uuid
    6.26 +
    6.27      def create_vif(self, xenapi_vif):
    6.28 -        
    6.29 +        """Create VIF device from the passed struct in Xen API format.
    6.30 +
    6.31 +        @param xenapi_vif: Xen API VIF Struct.
    6.32 +        @rtype: string
    6.33 +        @return: UUID
    6.34 +        """
    6.35          dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
    6.36          if not dev_uuid:
    6.37              raise XendError('Failed to create device')
     7.1 --- a/tools/python/xen/xend/XendStorageRepository.py	Thu Oct 12 18:51:17 2006 +0100
     7.2 +++ b/tools/python/xen/xend/XendStorageRepository.py	Fri Oct 13 15:13:21 2006 +0100
     7.3 @@ -31,7 +31,8 @@ XEND_STORAGE_MAX_IGNORE = -1
     7.4  XEND_STORAGE_DIR = "/var/lib/xend/storage/"
     7.5  XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
     7.6  XEND_STORAGE_IMG_FILENAME = "%s.img"
     7.7 -DF_COMMAND = "df -kl"
     7.8 +XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
     7.9 +DF_COMMAND = "df -lP"
    7.10  QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create %d %s %s"
    7.11  
    7.12  KB = 1024
    7.13 @@ -55,7 +56,7 @@ class XendStorageRepository:
    7.14          """
    7.15          @keyword storage_dir: Where the images will be stored.
    7.16          @type    storage_dir: string
    7.17 -        @keyword storage_max: Maximum disk space to use in KB.
    7.18 +        @keyword storage_max: Maximum disk space to use in bytes.
    7.19          @type    storage_max: int
    7.20  
    7.21          @ivar    storage_free: storage space free for this repository
    7.22 @@ -82,7 +83,7 @@ class XendStorageRepository:
    7.23      def _sr_uuid(self):
    7.24          uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid')
    7.25          try:
    7.26 -            if os.path.exists(uuid_file):
    7.27 +            if uuid_file and os.path.exists(uuid_file):
    7.28                  return open(uuid_file, 'r').read().strip()
    7.29              else:
    7.30                  new_uuid = uuid.createString()
    7.31 @@ -114,16 +115,25 @@ class XendStorageRepository:
    7.32                      if image_uuid not in self.images:
    7.33                          image_file = XEND_STORAGE_IMG_FILENAME % image_uuid
    7.34                          qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
    7.35 -                        image_path = os.path.join(XEND_STORAGE_DIR,
    7.36 -                                                  image_file)
    7.37 +                        cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
    7.38 +                        
    7.39 +                        image_path = os.path.join(XEND_STORAGE_DIR,image_file)
    7.40                          qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file)
    7.41 -                        image_size_kb = (os.stat(image_path).st_size)/1024
    7.42 +                        cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_file)
    7.43 +
    7.44 +                        qcow_size = os.stat(qcow_path).st_size
    7.45 +                        image_size = os.stat(image_path).st_size
    7.46  
    7.47                          vdi = XendQCOWVDI(image_uuid, self.uuid,
    7.48 -                                          qcow_path, image_path,
    7.49 -                                          image_size_kb, image_size_kb)
    7.50 +                                          qcow_path, image_path, cfg_path,
    7.51 +                                          image_size,
    7.52 +                                          qcow_size + image_size)
    7.53 +                        
    7.54 +                        if cfg_path and os.path.exists(cfg_path):
    7.55 +                            vdi.load_config(cfg_path)
    7.56 +                        
    7.57                          self.images[image_uuid] = vdi
    7.58 -                        total_used += image_size_kb
    7.59 +                        total_used += image_size
    7.60  
    7.61              # remove images that aren't valid
    7.62              for image_uuid in self.images.keys():
    7.63 @@ -147,7 +157,7 @@ class XendStorageRepository:
    7.64      def _get_df(self):
    7.65          """Returns the output of 'df' in a dictionary where the keys
    7.66          are the Linux device numbers, and the values are it's corresponding
    7.67 -        free space in KB.
    7.68 +        free space in bytes
    7.69  
    7.70          @rtype: dictionary
    7.71          """
    7.72 @@ -162,7 +172,7 @@ class XendStorageRepository:
    7.73          return devnum_free
    7.74  
    7.75      def _get_free_space(self):
    7.76 -        """Returns the amount of free space in KB available in the storage
    7.77 +        """Returns the amount of free space in bytes available in the storage
    7.78          partition. Note that this may not be used if the storage repository
    7.79          is initialised with a maximum size in storage_max.
    7.80  
    7.81 @@ -175,7 +185,7 @@ class XendStorageRepository:
    7.82          raise DeviceInvalidError("Device not found for storage path: %s" %
    7.83                                   self.storage_dir)
    7.84  
    7.85 -    def _has_space_available_for(self, size_kb):
    7.86 +    def _has_space_available_for(self, size_bytes):
    7.87          """Returns whether there is enough space for an image in the
    7.88          partition which the storage_dir resides on.
    7.89  
    7.90 @@ -184,15 +194,15 @@ class XendStorageRepository:
    7.91          if self.storage_max != -1:
    7.92              return self.storage_free
    7.93          
    7.94 -        kb_free = self._get_free_space()
    7.95 +        bytes_free = self._get_free_space()
    7.96          try:
    7.97 -            if size_kb < kb_free:
    7.98 +            if size_bytes < bytes_free:
    7.99                  return True
   7.100          except DeviceInvalidError:
   7.101              pass
   7.102          return False
   7.103  
   7.104 -    def create_image(self, desired_size_kb):
   7.105 +    def _create_image_files(self, desired_size_bytes):
   7.106          """Create an image and return its assigned UUID.
   7.107  
   7.108          @param desired_size_kb: Desired image size in KB.
   7.109 @@ -204,23 +214,28 @@ class XendStorageRepository:
   7.110          """
   7.111          self.lock.acquire()
   7.112          try:
   7.113 -            if not self._has_space_available_for(desired_size_kb):
   7.114 +            if not self._has_space_available_for(desired_size_bytes):
   7.115                  raise XendError("Not enough space")
   7.116  
   7.117              image_uuid = uuid.createString()
   7.118              # create file based image
   7.119              image_path = os.path.join(XEND_STORAGE_DIR,
   7.120                                        XEND_STORAGE_IMG_FILENAME % image_uuid)
   7.121 -            block = '\x00' * 1024
   7.122 +            
   7.123 +            if image_path and os.path.exists(image_path):
   7.124 +                raise XendError("Image with same UUID alreaady exists:" %
   7.125 +                                image_uuid)
   7.126 +            
   7.127 +            block = '\x00' * KB
   7.128              img = open(image_path, 'w')
   7.129 -            for i in range(desired_size_kb):
   7.130 +            for i in range(desired_size_bytes/KB):
   7.131                  img.write(block)
   7.132              img.close()
   7.133              
   7.134              # TODO: create qcow image
   7.135              qcow_path = os.path.join(XEND_STORAGE_DIR,
   7.136                                       XEND_STORAGE_QCOW_FILENAME % image_uuid)
   7.137 -            cmd = QCOW_CREATE_COMMAND % (desired_size_kb/1024,
   7.138 +            cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB,
   7.139                                           qcow_path, image_path)
   7.140  
   7.141              rc, output = commands.getstatusoutput(cmd)
   7.142 @@ -233,7 +248,7 @@ class XendStorageRepository:
   7.143              return image_uuid
   7.144          finally:
   7.145              self.lock.release()
   7.146 -        
   7.147 +
   7.148      def destroy_image(self, image_uuid):
   7.149          """Destroy an image that is managed by this storage repository.
   7.150  
   7.151 @@ -247,9 +262,12 @@ class XendStorageRepository:
   7.152                  # TODO: check if it is being used?
   7.153                  qcow_path = self.images[image_uuid].qcow_path
   7.154                  image_path = self.images[image_uuid].image_path
   7.155 +                cfg_path = self.images[image_uuid].cfg_path
   7.156                  try:
   7.157                      os.unlink(qcow_path)
   7.158                      os.unlink(image_path)
   7.159 +                    if cfg_path and os.path.exists(cfg_path):
   7.160 +                        os.unlink(cfg_path)
   7.161                  except OSError:
   7.162                      # TODO: log warning
   7.163                      pass
   7.164 @@ -272,7 +290,7 @@ class XendStorageRepository:
   7.165          finally:
   7.166              self.lock.release()
   7.167  
   7.168 -    def free_space_kb(self):
   7.169 +    def free_space_bytes(self):
   7.170          """Returns the amount of available space in KB.
   7.171          @rtype: int
   7.172          """
   7.173 @@ -282,7 +300,7 @@ class XendStorageRepository:
   7.174          finally:
   7.175              self.lock.release()
   7.176              
   7.177 -    def total_space_kb(self):
   7.178 +    def total_space_bytes(self):
   7.179          """Returns the total usable space of the storage repo in KB.
   7.180          @rtype: int
   7.181          """
   7.182 @@ -295,7 +313,7 @@ class XendStorageRepository:
   7.183          finally:
   7.184              self.lock.release()
   7.185              
   7.186 -    def used_space_kb(self):
   7.187 +    def used_space_bytes(self):
   7.188          """Returns the total amount of space used by this storage repository.
   7.189          @rtype: int
   7.190          """
   7.191 @@ -308,25 +326,72 @@ class XendStorageRepository:
   7.192          finally:
   7.193              self.lock.release()
   7.194  
   7.195 -    def used_space_bytes(self):
   7.196 -        return self.used_space_kb() * KB
   7.197 -    def free_space_bytes(self):
   7.198 -        return self.free_space_kb() * KB
   7.199 -    def total_space_bytes(self):
   7.200 -        return self.total_space_kb() * KB
   7.201 -
   7.202      def is_valid_vdi(self, vdi_uuid):
   7.203          return (vdi_uuid in self.images)
   7.204  
   7.205 +    def create_image(self, vdi_struct):
   7.206 +        image_uuid = None
   7.207 +        try:
   7.208 +            sector_count = int(vdi_struct.get('virtual_size', 0))
   7.209 +            sector_size = int(vdi_struct.get('sector_size', 1024))
   7.210 +            size_bytes = (sector_count * sector_size)
   7.211 +            
   7.212 +            image_uuid = self._create_image_files(size_bytes)
   7.213 +            image = self.images[image_uuid]
   7.214 +            image_cfg = {
   7.215 +                'sector_size': sector_size,
   7.216 +                'virtual_size': sector_count,
   7.217 +                'type': vdi_struct.get('type', 'system'),
   7.218 +                'name_label': vdi_struct.get('name_label', ''),
   7.219 +                'name_description': vdi_struct.get('name_description', ''),
   7.220 +                'sharable': bool(vdi_struct.get('sharable', False)),
   7.221 +                'read_only': bool(vdi_struct.get('read_only', False)),
   7.222 +            }
   7.223 +
   7.224 +            # load in configuration from vdi_struct
   7.225 +            image.load_config_dict(image_cfg)
   7.226 +
   7.227 +            # save configuration to file
   7.228 +            cfg_filename =  XEND_STORAGE_VDICFG_FILENAME % image_uuid
   7.229 +            cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_filename)
   7.230 +            image.save_config(cfg_path)
   7.231 +            
   7.232 +        except Exception, e:
   7.233 +            # cleanup before raising exception
   7.234 +            if image_uuid:
   7.235 +                self.destroy_image(image_uuid)
   7.236 +                
   7.237 +            raise
   7.238 +
   7.239 +        return image_uuid
   7.240 +        
   7.241 +    def xen_api_get_by_label(self, label):
   7.242 +        self.lock.acquire()
   7.243 +        try:
   7.244 +            for image_uuid, val in self.images.values():
   7.245 +                if val.name_label == label:
   7.246 +                    return image_uuid
   7.247 +            return None
   7.248 +        finally:
   7.249 +            self.lock.release()
   7.250 +
   7.251 +    def xen_api_get_by_uuid(self, image_uuid):
   7.252 +        self.lock.acquire()
   7.253 +        try:
   7.254 +            return self.images.get(image_uuid)
   7.255 +        finally:
   7.256 +            self.lock.release()        
   7.257 +    
   7.258 +
   7.259  # remove everything below this line!!
   7.260  if __name__ == "__main__":
   7.261      xsr = XendStorageRepository()
   7.262 -    print 'Free Space: %d MB' % (xsr.free_space_kb()/1024)
   7.263 +    print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB)
   7.264      print "Create Image:",
   7.265 -    print xsr.create_image(10 * 1024)
   7.266 +    print xsr._create_image_files(10 * MB)
   7.267      print 'Delete all images:'
   7.268      for image_uuid in xsr.list_images():
   7.269          print image_uuid,
   7.270 -        xsr.destroy_image(image_uuid)
   7.271 +        xsr._destroy_image_files(image_uuid)
   7.272  
   7.273      print
     8.1 --- a/tools/python/xen/xend/XendVDI.py	Thu Oct 12 18:51:17 2006 +0100
     8.2 +++ b/tools/python/xen/xend/XendVDI.py	Fri Oct 13 15:13:21 2006 +0100
     8.3 @@ -19,26 +19,119 @@
     8.4  # Representation of a Xen API VDI
     8.5  #
     8.6  
     8.7 +import os
     8.8 +
     8.9 +from xen.util.xmlrpclib2 import stringify
    8.10 +from xmlrpclib import dumps, loads
    8.11 +
    8.12  KB = 1024
    8.13  MB = 1024 * 1024
    8.14  
    8.15  class XendVDI:
    8.16 +    """Generic Xen API compatible VDI representation.
    8.17 +
    8.18 +    @cvar SAVED_CFG: list of configuration attributes to save.
    8.19 +    @cvar SAVED_CFG_INT: list of configurations that should be ints.
    8.20 +    """
    8.21 +    
    8.22 +    SAVED_CFG = ['name_label',
    8.23 +                 'name_description',
    8.24 +                 'sector_size',
    8.25 +                 'virtual_size',
    8.26 +                 'physical_utilisation',
    8.27 +                 'parent',
    8.28 +                 'children',
    8.29 +                 'sharable',
    8.30 +                 'read_only']
    8.31 +
    8.32 +    SAVED_CFG_INT = ['sector_size', 'virtual_size', 'physical_utilisation']
    8.33 +    
    8.34      def __init__(self, uuid, sr_uuid):
    8.35          self.uuid = uuid
    8.36          self.sr_uuid = sr_uuid
    8.37 +        self.name_label = ""
    8.38 +        self.name_description = ""
    8.39 +        self.sector_size = 1024
    8.40 +        self.virtual_size = 0
    8.41 +        self.physical_utilisation = 0
    8.42 +        self.parent = None
    8.43 +        self.children = []
    8.44 +        self.sharable = False
    8.45 +        self.read_only = False
    8.46 +        self.type = "system"
    8.47 +
    8.48 +        self.cfg_path = None
    8.49 +
    8.50 +    def load_config_dict(self, cfg):
    8.51 +        """Loads configuration into the object from a dict.
    8.52 +
    8.53 +        @param cfg: configuration dict
    8.54 +        @type  cfg: dict
    8.55 +        """
    8.56 +        for key in self.SAVED_CFG:
    8.57 +            if key in cfg:
    8.58 +                if key in self.SAVED_CFG_INT:
    8.59 +                    setattr(self, key, int(cfg[key]))
    8.60 +                else:
    8.61 +                    setattr(self, key, cfg[key])
    8.62 +
    8.63 +    def load_config(self, cfg_path):
    8.64 +        """Loads configuration from an XMLRPC parameter format.
    8.65 +
    8.66 +        @param cfg_path: configuration file path
    8.67 +        @type  cfg_path: type
    8.68 +        @rtype: bool
    8.69 +        @return: Successful or not.
    8.70 +        """
    8.71 +        try:
    8.72 +            cfg, _ = loads(open(cfg_path).read())
    8.73 +            cfg = cfg[0]
    8.74 +            self.load_config_dict(cfg)
    8.75 +            self.cfg_path = cfg_path
    8.76 +        except IOError, e:
    8.77 +            return False
    8.78 +        
    8.79 +        return True
    8.80 +
    8.81 +    def save_config(self, cfg_path = None):
    8.82 +        """Saves configuration at give path in XMLRPC parameter format.
    8.83 +
    8.84 +        If cfg_path is not give, it defaults to the where the VDI
    8.85 +        configuration as loaded if it load_config was called.
    8.86 +
    8.87 +        @keyword cfg_path: optional configuration file path
    8.88 +        @rtype: bool
    8.89 +        @return: Successful or not.
    8.90 +        """
    8.91 +        try:
    8.92 +            if not cfg_path and not self.cfg_path:
    8.93 +                return False
    8.94 +
    8.95 +            if not cfg_path:
    8.96 +                cfg_path = self.cfg_path
    8.97 +                
    8.98 +            cfg = {}
    8.99 +            for key in self.SAVED_CFG:
   8.100 +                try:
   8.101 +                    cfg[key] = getattr(self, key)
   8.102 +                except AttributeError:
   8.103 +                    pass
   8.104 +            open(cfg_path, 'w').write(dumps((stringify(cfg),),
   8.105 +                                            allow_none = True))
   8.106 +        except IOError, e:
   8.107 +            return False
   8.108 +
   8.109 +        return True
   8.110  
   8.111  class XendQCOWVDI(XendVDI):
   8.112 -    vdi_type = "system"
   8.113  
   8.114 -    def __init__(self, uuid, sr_uuid, qcow_path, image_path, vsize, psize):
   8.115 +    def __init__(self, uuid, sr_uuid, qcow_path, image_path, cfg_path,
   8.116 +                 vsize, psize):
   8.117          XendVDI.__init__(self, uuid, sr_uuid)
   8.118          self.qcow_path = qcow_path
   8.119          self.image_path = image_path
   8.120 -        self.vsize = vsize
   8.121 -        self.psize = psize
   8.122 +        self.cfg_path = cfg_path
   8.123 +        self.physical_utilisation = psize
   8.124 +        self.virtual_size = vsize
   8.125 +        self.sector_size = 1
   8.126  
   8.127 -    def get_physical_utilisation(self):
   8.128 -        return self.psize * KB
   8.129 -
   8.130 -    def get_virtual_size(self):
   8.131 -        return self.vsize * KB