ia64/xen-unstable

changeset 18642:9404bcb6d32e

Enhance XenAPI for pvSCSI

Basically, I implemented XenAPI for pvSCSI according to the patch of
XenAPI document which I sent before. However, I renamed the class
name of virtual SCSI devices to "DSCSI".

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Oct 16 09:50:18 2008 +0100 (2008-10-16)
parents 22c89412fc8c
children 819ab49deef1
files tools/python/xen/util/pci.py tools/python/xen/util/utils.py tools/python/xen/util/vscsi_util.py tools/python/xen/xend/XendAPI.py tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDSCSI.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/XendNode.py tools/python/xen/xend/XendPSCSI.py tools/python/xen/xend/server/vscsiif.py tools/python/xen/xm/create.dtd tools/python/xen/xm/main.py tools/python/xen/xm/xenapi_create.py
line diff
     1.1 --- a/tools/python/xen/util/pci.py	Wed Oct 15 15:58:09 2008 +0100
     1.2 +++ b/tools/python/xen/util/pci.py	Thu Oct 16 09:50:18 2008 +0100
     1.3 @@ -12,8 +12,8 @@ import re
     1.4  import types
     1.5  import struct
     1.6  import time
     1.7 +from xen.util import utils
     1.8  
     1.9 -PROC_MNT_PATH = '/proc/mounts'
    1.10  PROC_PCI_PATH = '/proc/bus/pci/devices'
    1.11  PROC_PCI_NUM_RESOURCES = 7
    1.12  
    1.13 @@ -97,9 +97,6 @@ MSIX_SIZE_MASK = 0x7ff
    1.14  # Global variable to store information from lspci
    1.15  lspci_info = None
    1.16  
    1.17 -# Global variable to store the sysfs mount point
    1.18 -sysfs_mnt_point = None
    1.19 -
    1.20  #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
    1.21  PAGE_SIZE = resource.getpagesize()
    1.22  PAGE_SHIFT = 0
    1.23 @@ -141,20 +138,8 @@ def parse_pci_name(pci_name_string):
    1.24   
    1.25  
    1.26  def find_sysfs_mnt():
    1.27 -    global sysfs_mnt_point
    1.28 -    if not sysfs_mnt_point is None:
    1.29 -        return sysfs_mnt_point
    1.30 -
    1.31      try:
    1.32 -        mounts_file = open(PROC_MNT_PATH,'r')
    1.33 -
    1.34 -        for line in mounts_file:
    1.35 -            sline = line.split()
    1.36 -            if len(sline)<3:
    1.37 -                continue
    1.38 -            if sline[2]=='sysfs':
    1.39 -                sysfs_mnt_point= sline[1]
    1.40 -                return sysfs_mnt_point
    1.41 +        return utils.find_sysfs_mount()
    1.42      except IOError, (errno, strerr):
    1.43          raise PciDeviceParseError(('Failed to locate sysfs mount: %s: %s (%d)'%
    1.44              (PROC_PCI_PATH, strerr, errno)))
     2.1 --- a/tools/python/xen/util/utils.py	Wed Oct 15 15:58:09 2008 +0100
     2.2 +++ b/tools/python/xen/util/utils.py	Thu Oct 16 09:50:18 2008 +0100
     2.3 @@ -48,3 +48,29 @@ def daemonize(prog, args, stdin_tmpfile=
     2.4      os.waitpid(pid, 0)
     2.5      return daemon_pid
     2.6  
     2.7 +# Global variable to store the sysfs mount point
     2.8 +sysfs_mount_point = None
     2.9 +
    2.10 +PROC_MOUNTS_PATH = '/proc/mounts'
    2.11 +
    2.12 +def find_sysfs_mount():
    2.13 +    global sysfs_mount_point
    2.14 +
    2.15 +    if not sysfs_mount_point is None:
    2.16 +        return sysfs_mount_point
    2.17 +
    2.18 +    try:
    2.19 +        mounts_file = open(PROC_MOUNTS_PATH, 'r')
    2.20 +
    2.21 +        for line in mounts_file:
    2.22 +            sline = line.split()
    2.23 +            if len(sline) < 3:
    2.24 +                continue
    2.25 +            if sline[2] == 'sysfs':
    2.26 +                sysfs_mount_point= sline[1]
    2.27 +                return sysfs_mount_point
    2.28 +    except IOError, (errno, strerr):
    2.29 +        raise
    2.30 +
    2.31 +    return None
    2.32 +
     3.1 --- a/tools/python/xen/util/vscsi_util.py	Wed Oct 15 15:58:09 2008 +0100
     3.2 +++ b/tools/python/xen/util/vscsi_util.py	Thu Oct 16 09:50:18 2008 +0100
     3.3 @@ -23,9 +23,18 @@
     3.4  """Support for VSCSI Devices.
     3.5  """
     3.6  import os
     3.7 +import os.path
     3.8  import sys
     3.9  import re
    3.10  import string
    3.11 +from xen.util import utils
    3.12 +
    3.13 +SYSFS_SCSI_PATH = "/bus/scsi/devices"
    3.14 +SYSFS_SCSI_DEV_VENDOR_PATH = '/vendor'
    3.15 +SYSFS_SCSI_DEV_MODEL_PATH = '/model'
    3.16 +SYSFS_SCSI_DEV_TYPEID_PATH = '/type'
    3.17 +SYSFS_SCSI_DEV_REVISION_PATH = '/rev'
    3.18 +SYSFS_SCSI_DEV_SCSILEVEL_PATH = '/scsi_level'
    3.19  
    3.20  def _vscsi_hctl_block(name, scsi_devices):
    3.21      """ block-device name is convert into hctl. (e.g., '/dev/sda',
    3.22 @@ -84,10 +93,10 @@ def _vscsi_block_scsiid_to_hctl(phyname,
    3.23  def vscsi_get_scsidevices():
    3.24      """ get all scsi devices"""
    3.25  
    3.26 -    SERCH_SCSI_PATH = "/sys/bus/scsi/devices"
    3.27      devices = []
    3.28 +    sysfs_mnt = utils.find_sysfs_mount() 
    3.29  
    3.30 -    for dirpath, dirnames, files in os.walk(SERCH_SCSI_PATH):
    3.31 +    for dirpath, dirnames, files in os.walk(sysfs_mnt + SYSFS_SCSI_PATH):
    3.32          for hctl in dirnames:
    3.33              paths = os.path.join(dirpath, hctl)
    3.34              block = "-"
    3.35 @@ -131,3 +140,101 @@ def vscsi_search_hctl_and_block(device):
    3.36  
    3.37      return (hctl, block)
    3.38  
    3.39 +
    3.40 +def get_scsi_vendor(pHCTL):
    3.41 +    try:
    3.42 +        sysfs_mnt = utils.find_sysfs_mount() 
    3.43 +        sysfs_scsi_dev_path = \
    3.44 +            os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL)
    3.45 +        scsi_vendor = \
    3.46 +            os.popen('cat ' + sysfs_scsi_dev_path + \
    3.47 +                              SYSFS_SCSI_DEV_VENDOR_PATH).read()
    3.48 +        return scsi_vendor.splitlines()[0]
    3.49 +    except:
    3.50 +        return None
    3.51 +
    3.52 +def get_scsi_model(pHCTL):
    3.53 +    try:
    3.54 +        sysfs_mnt = utils.find_sysfs_mount() 
    3.55 +        sysfs_scsi_dev_path = \
    3.56 +            os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL)
    3.57 +        scsi_model = \
    3.58 +            os.popen('cat ' + sysfs_scsi_dev_path + \
    3.59 +                              SYSFS_SCSI_DEV_MODEL_PATH).read()
    3.60 +        return scsi_model.splitlines()[0]
    3.61 +    except:
    3.62 +        return None
    3.63 +
    3.64 +def get_scsi_typeid(pHCTL):
    3.65 +    try:
    3.66 +        sysfs_mnt = utils.find_sysfs_mount() 
    3.67 +        sysfs_scsi_dev_path = \
    3.68 +            os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL)
    3.69 +        scsi_typeid = \
    3.70 +            os.popen('cat ' + sysfs_scsi_dev_path + \
    3.71 +                              SYSFS_SCSI_DEV_TYPEID_PATH).read()
    3.72 +        return int(scsi_typeid.splitlines()[0])
    3.73 +    except:
    3.74 +        return None
    3.75 +
    3.76 +def get_scsi_revision(pHCTL):
    3.77 +    try:
    3.78 +        sysfs_mnt = utils.find_sysfs_mount() 
    3.79 +        sysfs_scsi_dev_path = \
    3.80 +            os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL)
    3.81 +        scsi_revision = \
    3.82 +            os.popen('cat ' + sysfs_scsi_dev_path + \
    3.83 +                              SYSFS_SCSI_DEV_REVISION_PATH).read()
    3.84 +        return scsi_revision.splitlines()[0]
    3.85 +    except:
    3.86 +        return None
    3.87 +
    3.88 +def get_scsi_scsilevel(pHCTL):
    3.89 +    try:
    3.90 +        sysfs_mnt = utils.find_sysfs_mount() 
    3.91 +        sysfs_scsi_dev_path = \
    3.92 +            os.path.join(sysfs_mnt + SYSFS_SCSI_PATH, pHCTL)
    3.93 +        scsi_scsilevel = \
    3.94 +            os.popen('cat ' + sysfs_scsi_dev_path + \
    3.95 +                              SYSFS_SCSI_DEV_SCSILEVEL_PATH).read()
    3.96 +        return int(scsi_scsilevel.splitlines()[0])
    3.97 +    except:
    3.98 +        return None
    3.99 +
   3.100 +def get_all_scsi_devices():
   3.101 +
   3.102 +    scsi_devs = []
   3.103 +
   3.104 +    for scsi_info in vscsi_get_scsidevices():
   3.105 +        scsi_dev = {
   3.106 +            'physical_HCTL': scsi_info[0],
   3.107 +            'dev_name': None,
   3.108 +            'sg_name': scsi_info[2],
   3.109 +            'scsi_id': None
   3.110 +        }
   3.111 +        if scsi_info[1] != '-':
   3.112 +            scsi_dev['dev_name'] = scsi_info[1] 
   3.113 +        if scsi_info[3] != '-':
   3.114 +            scsi_dev['scsi_id'] = scsi_info[3] 
   3.115 +
   3.116 +        scsi_dev['vendor_name'] = \
   3.117 +            get_scsi_vendor(scsi_dev['physical_HCTL'])
   3.118 +        scsi_dev['model'] = \
   3.119 +            get_scsi_model(scsi_dev['physical_HCTL'])
   3.120 +        scsi_dev['type_id'] = \
   3.121 +            get_scsi_typeid(scsi_dev['physical_HCTL'])
   3.122 +        scsi_dev['revision'] = \
   3.123 +            get_scsi_revision(scsi_dev['physical_HCTL'])
   3.124 +        scsi_dev['scsi_level'] = \
   3.125 +            get_scsi_scsilevel(scsi_dev['physical_HCTL'])
   3.126 +
   3.127 +        try:
   3.128 +            lsscsi_info = os.popen('lsscsi ' + scsi_dev['physical_HCTL']).read().split()
   3.129 +            scsi_dev['type'] = lsscsi_info[1]
   3.130 +        except:
   3.131 +            scsi_dev['type'] = None
   3.132 +
   3.133 +        scsi_devs.append(scsi_dev)
   3.134 +
   3.135 +    return scsi_devs
   3.136 +
     4.1 --- a/tools/python/xen/xend/XendAPI.py	Wed Oct 15 15:58:09 2008 +0100
     4.2 +++ b/tools/python/xen/xend/XendAPI.py	Thu Oct 16 09:50:18 2008 +0100
     4.3 @@ -42,6 +42,8 @@ from XendPIF import XendPIF
     4.4  from XendPBD import XendPBD
     4.5  from XendPPCI import XendPPCI
     4.6  from XendDPCI import XendDPCI
     4.7 +from XendPSCSI import XendPSCSI
     4.8 +from XendDSCSI import XendDSCSI
     4.9  from XendXSPolicy import XendXSPolicy, XendACMPolicy
    4.10  
    4.11  from XendAPIConstants import *
    4.12 @@ -480,7 +482,9 @@ classes = {
    4.13      'PBD'          : valid_object("PBD"),
    4.14      'PIF_metrics'  : valid_object("PIF_metrics"),
    4.15      'PPCI'         : valid_object("PPCI"),
    4.16 -    'DPCI'         : valid_object("DPCI")
    4.17 +    'DPCI'         : valid_object("DPCI"),
    4.18 +    'PSCSI'        : valid_object("PSCSI"),
    4.19 +    'DSCSI'        : valid_object("DSCSI")
    4.20  }
    4.21  
    4.22  autoplug_classes = {
    4.23 @@ -491,6 +495,8 @@ autoplug_classes = {
    4.24      'PIF_metrics' : XendPIFMetrics,
    4.25      'PPCI'        : XendPPCI,
    4.26      'DPCI'        : XendDPCI,
    4.27 +    'PSCSI'       : XendPSCSI,
    4.28 +    'DSCSI'       : XendDSCSI,
    4.29      'XSPolicy'    : XendXSPolicy,
    4.30      'ACMPolicy'   : XendACMPolicy,
    4.31  }
    4.32 @@ -881,6 +887,7 @@ class XendAPI(object):
    4.33                      'PBDs',
    4.34                      'PIFs',
    4.35                      'PPCIs',
    4.36 +                    'PSCSIs',
    4.37                      'host_CPUs',
    4.38                      'cpu_configuration',
    4.39                      'metrics',
    4.40 @@ -961,6 +968,8 @@ class XendAPI(object):
    4.41          return xen_api_success(XendNode.instance().get_PIF_refs())
    4.42      def host_get_PPCIs(self, session, ref):
    4.43          return xen_api_success(XendNode.instance().get_PPCI_refs())
    4.44 +    def host_get_PSCSIs(self, session, ref):
    4.45 +        return xen_api_success(XendNode.instance().get_PSCSI_refs())
    4.46      def host_get_host_CPUs(self, session, host_ref):
    4.47          return xen_api_success(XendNode.instance().get_host_cpu_refs())
    4.48      def host_get_metrics(self, _, ref):
    4.49 @@ -1037,7 +1046,8 @@ class XendAPI(object):
    4.50                    'logging': {},
    4.51                    'PIFs': XendPIF.get_all(),
    4.52                    'PBDs': XendPBD.get_all(),
    4.53 -                  'PPCIs': XendPPCI.get_all()}
    4.54 +                  'PPCIs': XendPPCI.get_all(),
    4.55 +                  'PSCSIs': XendPSCSI.get_all()}
    4.56          return xen_api_success(record)
    4.57  
    4.58      # class methods
    4.59 @@ -1158,6 +1168,7 @@ class XendAPI(object):
    4.60                    'VBDs',
    4.61                    'VTPMs',
    4.62                    'DPCIs',
    4.63 +                  'DSCSIs',
    4.64                    'tools_version',
    4.65                    'domid',
    4.66                    'is_control_domain',
    4.67 @@ -1304,6 +1315,10 @@ class XendAPI(object):
    4.68          dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
    4.69          return xen_api_success(dom.get_dpcis())
    4.70      
    4.71 +    def VM_get_DSCSIs(self, session, vm_ref):
    4.72 +        dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
    4.73 +        return xen_api_success(dom.get_dscsis())
    4.74 +
    4.75      def VM_get_tools_version(self, session, vm_ref):
    4.76          dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
    4.77          return dom.get_tools_version()
    4.78 @@ -1684,6 +1699,7 @@ class XendAPI(object):
    4.79              'VBDs': xeninfo.get_vbds(),
    4.80              'VTPMs': xeninfo.get_vtpms(),
    4.81              'DPCIs': xeninfo.get_dpcis(),
    4.82 +            'DSCSIs': xeninfo.get_dscsis(),
    4.83              'PV_bootloader': xeninfo.info.get('PV_bootloader'),
    4.84              'PV_kernel': xeninfo.info.get('PV_kernel'),
    4.85              'PV_ramdisk': xeninfo.info.get('PV_ramdisk'),
     5.1 --- a/tools/python/xen/xend/XendConfig.py	Wed Oct 15 15:58:09 2008 +0100
     5.2 +++ b/tools/python/xen/xend/XendConfig.py	Thu Oct 16 09:50:18 2008 +0100
     5.3 @@ -26,6 +26,8 @@ from xen.xend import XendOptions
     5.4  from xen.xend import XendAPIStore
     5.5  from xen.xend.XendPPCI import XendPPCI
     5.6  from xen.xend.XendDPCI import XendDPCI
     5.7 +from xen.xend.XendPSCSI import XendPSCSI
     5.8 +from xen.xend.XendDSCSI import XendDSCSI
     5.9  from xen.xend.XendError import VmError
    5.10  from xen.xend.XendDevices import XendDevices
    5.11  from xen.xend.PrettyPrint import prettyprintstring
    5.12 @@ -782,8 +784,8 @@ class XendConfig(dict):
    5.13          log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
    5.14  
    5.15          # _parse_sxp() below will call device_add() and construct devices.
    5.16 -        # Some devices (currently only pci) may require VM's uuid, so
    5.17 -        # setup self['uuid'] beforehand.
    5.18 +        # Some devices may require VM's uuid, so setup self['uuid']
    5.19 +        # beforehand.
    5.20          self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
    5.21  
    5.22          cfg = self._parse_sxp(sxp_cfg)
    5.23 @@ -1222,29 +1224,28 @@ class XendConfig(dict):
    5.24              dev_type = sxp.name(config)
    5.25              dev_info = {}
    5.26  
    5.27 -            if dev_type == 'pci' or dev_type == 'vscsi':
    5.28 +            if dev_type == 'pci':
    5.29                  pci_devs_uuid = sxp.child_value(config, 'uuid',
    5.30                                                  uuid.createString())
    5.31  
    5.32                  pci_dict = self.pci_convert_sxp_to_dict(config)
    5.33                  pci_devs = pci_dict['devs']
    5.34  
    5.35 -                if dev_type != 'vscsi':
    5.36 -                    # create XenAPI DPCI objects.
    5.37 -                    for pci_dev in pci_devs:
    5.38 -                        dpci_uuid = pci_dev.get('uuid')
    5.39 -                        ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
    5.40 -                                                        pci_dev['bus'],
    5.41 -                                                        pci_dev['slot'],
    5.42 -                                                        pci_dev['func'])
    5.43 -                        if ppci_uuid is None:
    5.44 -                            continue
    5.45 -                        dpci_record = {
    5.46 -                            'VM': self['uuid'],
    5.47 -                            'PPCI': ppci_uuid,
    5.48 -                            'hotplug_slot': pci_dev.get('vslot', 0)
    5.49 -                        }
    5.50 -                        XendDPCI(dpci_uuid, dpci_record)
    5.51 +                # create XenAPI DPCI objects.
    5.52 +                for pci_dev in pci_devs:
    5.53 +                    dpci_uuid = pci_dev.get('uuid')
    5.54 +                    ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
    5.55 +                                                     pci_dev['bus'],
    5.56 +                                                     pci_dev['slot'],
    5.57 +                                                     pci_dev['func'])
    5.58 +                    if ppci_uuid is None:
    5.59 +                        continue
    5.60 +                    dpci_record = {
    5.61 +                        'VM': self['uuid'],
    5.62 +                        'PPCI': ppci_uuid,
    5.63 +                        'hotplug_slot': pci_dev.get('vslot', 0)
    5.64 +                    }
    5.65 +                    XendDPCI(dpci_uuid, dpci_record)
    5.66  
    5.67                  target['devices'][pci_devs_uuid] = (dev_type,
    5.68                                                      {'devs': pci_devs,
    5.69 @@ -1254,6 +1255,30 @@ class XendConfig(dict):
    5.70  
    5.71                  return pci_devs_uuid
    5.72  
    5.73 +            if dev_type == 'vscsi':
    5.74 +                vscsi_devs_uuid = sxp.child_value(config, 'uuid',
    5.75 +                                                  uuid.createString())
    5.76 +                vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
    5.77 +                vscsi_devs = vscsi_dict['devs']
    5.78 +
    5.79 +                # create XenAPI DSCSI objects.
    5.80 +                for vscsi_dev in vscsi_devs:
    5.81 +                    dscsi_uuid = vscsi_dev.get('uuid')
    5.82 +                    pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
    5.83 +                    if pscsi_uuid is None:
    5.84 +                        continue
    5.85 +                    dscsi_record = {
    5.86 +                        'VM': self['uuid'],
    5.87 +                        'PSCSI': pscsi_uuid,
    5.88 +                        'virtual_HCTL': vscsi_dev.get('v-dev')
    5.89 +                    }
    5.90 +                    XendDSCSI(dscsi_uuid, dscsi_record)
    5.91 +
    5.92 +                target['devices'][vscsi_devs_uuid] = \
    5.93 +                    (dev_type, {'devs': vscsi_devs, 'uuid': vscsi_devs_uuid} )
    5.94 +                log.debug("XendConfig: reading device: %s" % vscsi_devs)
    5.95 +                return vscsi_devs_uuid
    5.96 +
    5.97              for opt_val in config[1:]:
    5.98                  try:
    5.99                      opt, val = opt_val
   5.100 @@ -1559,6 +1584,86 @@ class XendConfig(dict):
   5.101  
   5.102          return dev_config
   5.103  
   5.104 +    def vscsi_convert_sxp_to_dict(self, dev_sxp):
   5.105 +        """Convert vscsi device sxp to dict
   5.106 +        @param dev_sxp: device configuration
   5.107 +        @type  dev_sxp: SXP object (parsed config)
   5.108 +        @return: dev_config
   5.109 +        @rtype: dictionary
   5.110 +        """
   5.111 +        # Parsing the device SXP's. In most cases, the SXP looks
   5.112 +        # like this:
   5.113 +        #
   5.114 +        # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
   5.115 +        #
   5.116 +        # However, for SCSI devices it looks like this:
   5.117 +        #
   5.118 +        # [device,
   5.119 +        #   [vscsi,
   5.120 +        #     [dev,
   5.121 +        #       [devid, 0], [p-devname, sdb], [p-dev, 1:0:0:1],
   5.122 +        #       [v-dev, 0:0:0:0], [state, Initialising]
   5.123 +        #     ],
   5.124 +        #     [dev,
   5.125 +        #       [devid, 0], [p-devname, sdc], [p-dev, 1:0:0:2],
   5.126 +        #       [v-dev, 0:0:0:1], [satet, Initialising]
   5.127 +        #     ]
   5.128 +        #   ],
   5.129 +        #   [vscsi,
   5.130 +        #     [dev,
   5.131 +        #       [devid, 1], [p-devname, sdg], [p-dev, 2:0:0:0],
   5.132 +        #       [v-dev, 1:0:0:0], [state, Initialising]
   5.133 +        #     ],
   5.134 +        #     [dev,
   5.135 +        #       [devid, 1], [p-devname, sdh], [p-dev, 2:0:0:1],
   5.136 +        #       [v-dev, 1:0:0:1], [satet, Initialising]
   5.137 +        #     ]
   5.138 +        #   ]
   5.139 +        # ]
   5.140 +        #
   5.141 +        # It seems the reasoning for this difference is because
   5.142 +        # vscsiif.py needs all the SCSI device configurations with 
   5.143 +        # same host number at the same time when creating the devices.
   5.144 +
   5.145 +        # For SCSI device hotplug support, the SXP of SCSI devices is
   5.146 +        # extendend like this:
   5.147 +        #
   5.148 +        # [device,
   5.149 +        #   [vscsi,
   5.150 +        #     [dev,
   5.151 +        #       [devid, 0], [p-devname, sdd], [p-dev, 1:0:0:3],
   5.152 +        #       [v-dev, 0:0:0:2], [state, Initialising]
   5.153 +        #     ]
   5.154 +        #   ]
   5.155 +        # ]
   5.156 +        #
   5.157 +        # state 'Initialising' indicates that the device is being attached,
   5.158 +        # while state 'Closing' indicates that the device is being detached.
   5.159 +        #
   5.160 +        # The Dict looks like this:
   5.161 +        #
   5.162 +        # { devs: [ {devid: 0, p-devname: sdd, p-dev: 1:0:0:3,
   5.163 +        #            v-dev: 0:0:0:2, state: Initialising} ] }
   5.164 +
   5.165 +        dev_config = {}
   5.166 +
   5.167 +        vscsi_devs = []
   5.168 +        for vscsi_dev in sxp.children(dev_sxp, 'dev'):
   5.169 +            vscsi_dev_info = {}
   5.170 +            for opt_val in vscsi_dev[1:]:
   5.171 +                try:
   5.172 +                    opt, val = opt_val
   5.173 +                    vscsi_dev_info[opt] = val
   5.174 +                except TypeError:
   5.175 +                    pass
   5.176 +            # append uuid for each vscsi device.
   5.177 +            vscsi_uuid = vscsi_dev_info.get('uuid', uuid.createString())
   5.178 +            vscsi_dev_info['uuid'] = vscsi_uuid
   5.179 +            vscsi_devs.append(vscsi_dev_info)
   5.180 +        dev_config['devs'] = vscsi_devs 
   5.181 +
   5.182 +        return dev_config
   5.183 +
   5.184      def console_add(self, protocol, location, other_config = {}):
   5.185          dev_uuid = uuid.createString()
   5.186          if protocol == 'vt100':
   5.187 @@ -1632,7 +1737,7 @@ class XendConfig(dict):
   5.188  
   5.189              dev_type, dev_info = self['devices'][dev_uuid]
   5.190  
   5.191 -            if dev_type == 'pci' or dev_type == 'vscsi': # Special case for pci
   5.192 +            if dev_type == 'pci': # Special case for pci
   5.193                  pci_dict = self.pci_convert_sxp_to_dict(config)
   5.194                  pci_devs = pci_dict['devs']
   5.195  
   5.196 @@ -1640,28 +1745,52 @@ class XendConfig(dict):
   5.197                  for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
   5.198                      XendAPIStore.deregister(dpci_uuid, "DPCI")
   5.199  
   5.200 -                if dev_type != 'vscsi':
   5.201 -                    # create XenAPI DPCI objects.
   5.202 -                    for pci_dev in pci_devs:
   5.203 -                        dpci_uuid = pci_dev.get('uuid')
   5.204 -                        ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
   5.205 -                                                         pci_dev['bus'],
   5.206 -                                                         pci_dev['slot'],
   5.207 -                                                         pci_dev['func'])
   5.208 -                        if ppci_uuid is None:
   5.209 -                            continue
   5.210 -                        dpci_record = {
   5.211 -                            'VM': self['uuid'],
   5.212 -                            'PPCI': ppci_uuid,
   5.213 -                            'hotplug_slot': pci_dev.get('vslot', 0)
   5.214 -                        }
   5.215 -                        XendDPCI(dpci_uuid, dpci_record)
   5.216 +                # create XenAPI DPCI objects.
   5.217 +                for pci_dev in pci_devs:
   5.218 +                    dpci_uuid = pci_dev.get('uuid')
   5.219 +                    ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
   5.220 +                                                     pci_dev['bus'],
   5.221 +                                                     pci_dev['slot'],
   5.222 +                                                     pci_dev['func'])
   5.223 +                    if ppci_uuid is None:
   5.224 +                        continue
   5.225 +                    dpci_record = {
   5.226 +                        'VM': self['uuid'],
   5.227 +                        'PPCI': ppci_uuid,
   5.228 +                        'hotplug_slot': pci_dev.get('vslot', 0)
   5.229 +                    }
   5.230 +                    XendDPCI(dpci_uuid, dpci_record)
   5.231  
   5.232                  self['devices'][dev_uuid] = (dev_type,
   5.233                                               {'devs': pci_devs,
   5.234                                                'uuid': dev_uuid})
   5.235                  return True
   5.236                  
   5.237 +            if dev_type == 'vscsi': # Special case for vscsi
   5.238 +                vscsi_dict = self.vscsi_convert_sxp_to_dict(config)
   5.239 +                vscsi_devs = vscsi_dict['devs']
   5.240 +
   5.241 +                # destroy existing XenAPI DSCSI objects
   5.242 +                for dscsi_uuid in XendDSCSI.get_by_VM(self['uuid']):
   5.243 +                    XendAPIStore.deregister(dscsi_uuid, "DSCSI")
   5.244 +
   5.245 +                # create XenAPI DSCSI objects.
   5.246 +                for vscsi_dev in vscsi_devs:
   5.247 +                    dscsi_uuid = vscsi_dev.get('uuid')
   5.248 +                    pscsi_uuid = XendPSCSI.get_by_HCTL(vscsi_dev['p-dev'])
   5.249 +                    if pscsi_uuid is None:
   5.250 +                        continue
   5.251 +                    dscsi_record = {
   5.252 +                        'VM': self['uuid'],
   5.253 +                        'PSCSI': pscsi_uuid,
   5.254 +                        'virtual_HCTL': vscsi_dev.get('v-dev')
   5.255 +                    }
   5.256 +                    XendDSCSI(dscsi_uuid, dscsi_record)
   5.257 +
   5.258 +                self['devices'][dev_uuid] = \
   5.259 +                    (dev_type, {'devs': vscsi_devs, 'uuid': dev_uuid} )
   5.260 +                return True
   5.261 +                
   5.262              for opt_val in config[1:]:
   5.263                  try:
   5.264                      opt, val = opt_val
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/tools/python/xen/xend/XendDSCSI.py	Thu Oct 16 09:50:18 2008 +0100
     6.3 @@ -0,0 +1,174 @@
     6.4 +#============================================================================
     6.5 +# This library is free software; you can redistribute it and/or
     6.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     6.7 +# License as published by the Free Software Foundation.
     6.8 +#
     6.9 +# This library is distributed in the hope that it will be useful,
    6.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.12 +# Lesser General Public License for more details.
    6.13 +#
    6.14 +# You should have received a copy of the GNU Lesser General Public
    6.15 +# License along with this library; if not, write to the Free Software
    6.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.17 +#============================================================================
    6.18 +# Copyright FUJITSU LIMITED 2008
    6.19 +#       Masaki Kanno <kanno.masaki@jp.fujitsu.com>
    6.20 +#============================================================================
    6.21 +
    6.22 +from xen.xend.XendBase import XendBase
    6.23 +from xen.xend.XendPSCSI import XendPSCSI
    6.24 +from xen.xend import XendAPIStore
    6.25 +from xen.xend import sxp
    6.26 +from xen.xend import uuid as genuuid
    6.27 +
    6.28 +import XendDomain, XendNode
    6.29 +
    6.30 +from XendError import *
    6.31 +from XendTask import XendTask
    6.32 +from XendLogging import log
    6.33 +
    6.34 +class XendDSCSI(XendBase):
    6.35 +    """Representation of a half-virtualized SCSI device."""
    6.36 +
    6.37 +    def getClass(self):
    6.38 +        return "DSCSI"
    6.39 +
    6.40 +    def getAttrRO(self):
    6.41 +        attrRO = ['VM',
    6.42 +                  'PSCSI',
    6.43 +                  'virtual_host',
    6.44 +                  'virtual_channel',
    6.45 +                  'virtual_target',
    6.46 +                  'virtual_lun',
    6.47 +                  'virtual_HCTL',
    6.48 +                  'runtime_properties']
    6.49 +        return XendBase.getAttrRO() + attrRO
    6.50 +
    6.51 +    def getAttrRW(self):
    6.52 +        attrRW = []
    6.53 +        return XendBase.getAttrRW() + attrRW
    6.54 +
    6.55 +    def getAttrInst(self):
    6.56 +        attrInst = ['VM',
    6.57 +                    'PSCSI',
    6.58 +                    'virtual_HCTL']
    6.59 +        return XendBase.getAttrInst() + attrInst
    6.60 +
    6.61 +    def getMethods(self):
    6.62 +        methods = ['destroy']
    6.63 +        return XendBase.getMethods() + methods
    6.64 +
    6.65 +    def getFuncs(self):
    6.66 +        funcs = ['create']
    6.67 +        return XendBase.getFuncs() + funcs
    6.68 +
    6.69 +    getClass    = classmethod(getClass)
    6.70 +    getAttrRO   = classmethod(getAttrRO)
    6.71 +    getAttrRW   = classmethod(getAttrRW)
    6.72 +    getAttrInst = classmethod(getAttrInst)
    6.73 +    getMethods  = classmethod(getMethods)
    6.74 +    getFuncs    = classmethod(getFuncs)
    6.75 + 
    6.76 +    def create(self, dscsi_struct):
    6.77 +
    6.78 +        # Check if VM is valid
    6.79 +        xendom = XendDomain.instance()
    6.80 +        if not xendom.is_valid_vm(dscsi_struct['VM']):
    6.81 +            raise InvalidHandleError('VM', dscsi_struct['VM'])
    6.82 +        dom = xendom.get_vm_by_uuid(dscsi_struct['VM'])
    6.83 +
    6.84 +        # Check if PSCSI is valid
    6.85 +        xennode = XendNode.instance()
    6.86 +        pscsi_uuid = xennode.get_pscsi_by_uuid(dscsi_struct['PSCSI'])
    6.87 +        if not pscsi_uuid:
    6.88 +            raise InvalidHandleError('PSCSI', dscsi_struct['PSCSI'])
    6.89 +
    6.90 +        # Assign PSCSI to VM
    6.91 +        try:
    6.92 +            dscsi_ref = XendTask.log_progress(0, 100, \
    6.93 +                                              dom.create_dscsi, \
    6.94 +                                              dscsi_struct)
    6.95 +        except XendError, e:
    6.96 +            log.exception("Error in create_dscsi")
    6.97 +            raise
    6.98 +
    6.99 +        return dscsi_ref
   6.100 +
   6.101 +    create = classmethod(create)
   6.102 +
   6.103 +    def get_by_VM(cls, VM_ref):
   6.104 +        result = []
   6.105 +        for dscsi in XendAPIStore.get_all("DSCSI"):
   6.106 +            if dscsi.get_VM() == VM_ref:
   6.107 +                result.append(dscsi.get_uuid())
   6.108 +        return result
   6.109 +
   6.110 +    get_by_VM = classmethod(get_by_VM)
   6.111 +
   6.112 +    def __init__(self, uuid, record):
   6.113 +        XendBase.__init__(self, uuid, record)
   6.114 +        v_hctl = self.virtual_HCTL.split(':')
   6.115 +        self.virtual_host = int(v_hctl[0])
   6.116 +        self.virtual_channel = int(v_hctl[1])
   6.117 +        self.virtual_target = int(v_hctl[2])
   6.118 +        self.virtual_lun = int(v_hctl[3])
   6.119 +
   6.120 +    def get_VM(self):
   6.121 +        return self.VM
   6.122 +
   6.123 +    def get_PSCSI(self):
   6.124 +        return self.PSCSI
   6.125 +
   6.126 +    def get_virtual_host(self):
   6.127 +        return self.virtual_host
   6.128 +
   6.129 +    def get_virtual_channel(self):
   6.130 +        return self.virtual_channel
   6.131 +
   6.132 +    def get_virtual_target(self):
   6.133 +        return self.virtual_target
   6.134 +
   6.135 +    def get_virtual_lun(self):
   6.136 +        return self.virtual_lun
   6.137 +
   6.138 +    def get_virtual_HCTL(self):
   6.139 +        return self.virtual_HCTL
   6.140 +
   6.141 +    def get_runtime_properties(self):
   6.142 +        xendom = XendDomain.instance()
   6.143 +        dominfo = xendom.get_vm_by_uuid(self.VM)
   6.144 +
   6.145 +        try:
   6.146 +            device_dict = {}
   6.147 +            for device_sxp in dominfo.getDeviceSxprs('vscsi'):
   6.148 +                target_dev = None
   6.149 +                for dev in device_sxp[1][0][1]:
   6.150 +                    vdev = sxp.child_value(dev, 'v-dev')
   6.151 +                    if vdev == self.virtual_HCTL:
   6.152 +                        target_dev = dev
   6.153 +                        break
   6.154 +                if target_dev is None:
   6.155 +                    continue
   6.156 +
   6.157 +                dev_dict = {}
   6.158 +                for info in target_dev[1:]:
   6.159 +                    dev_dict[info[0]] = info[1]
   6.160 +                device_dict['dev'] = dev_dict
   6.161 +                for info in device_sxp[1][1:]:
   6.162 +                    device_dict[info[0]] = info[1]
   6.163 +
   6.164 +            return device_dict
   6.165 +        except Exception, exn:
   6.166 +            log.exception(exn)
   6.167 +            return {}
   6.168 +
   6.169 +    def destroy(self):
   6.170 +        xendom = XendDomain.instance()
   6.171 +        dom = xendom.get_vm_by_uuid(self.get_VM())
   6.172 +        if not dom:
   6.173 +            raise InvalidHandleError("VM", self.get_VM())
   6.174 +        XendTask.log_progress(0, 100, \
   6.175 +                              dom.destroy_dscsi, \
   6.176 +                              self.get_uuid())
   6.177 +
     7.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Wed Oct 15 15:58:09 2008 +0100
     7.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Thu Oct 16 09:50:18 2008 +0100
     7.3 @@ -55,9 +55,11 @@ from xen.xend.XendAPIConstants import *
     7.4  
     7.5  from xen.xend.XendVMMetrics import XendVMMetrics
     7.6  
     7.7 +from xen.xend import XendAPIStore
     7.8  from xen.xend.XendPPCI import XendPPCI
     7.9  from xen.xend.XendDPCI import XendDPCI
    7.10 -from xen.xend import XendAPIStore
    7.11 +from xen.xend.XendPSCSI import XendPSCSI
    7.12 +from xen.xend.XendDSCSI import XendDSCSI
    7.13  
    7.14  MIGRATE_TIMEOUT = 30.0
    7.15  BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
    7.16 @@ -663,6 +665,9 @@ class XendDomainInfo:
    7.17                  if dev_type == 'pci':
    7.18                      for dev in dev_config_dict['devs']:
    7.19                          XendAPIStore.deregister(dev['uuid'], 'DPCI')
    7.20 +                if dev_type == 'vscsi':
    7.21 +                    for dev in dev_config_dict['devs']:
    7.22 +                        XendAPIStore.deregister(dev['uuid'], 'DSCSI')
    7.23                  elif dev_type == 'tap':
    7.24                      self.info['vbd_refs'].remove(dev_uuid)
    7.25                  else:
    7.26 @@ -786,12 +791,11 @@ class XendDomainInfo:
    7.27          if dev_class != 'vscsi':
    7.28              return False
    7.29  
    7.30 -        dev_config = self.info.pci_convert_sxp_to_dict(dev_sxp)
    7.31 +        dev_config = self.info.vscsi_convert_sxp_to_dict(dev_sxp)
    7.32          dev = dev_config['devs'][0]
    7.33 -        req_devid = sxp.child_value(dev_sxp, 'devid')
    7.34 -        req_devid = int(req_devid)
    7.35 +        req_devid = int(dev['devid'])
    7.36          existing_dev_info = self._getDeviceInfo_vscsi(req_devid, dev['v-dev'])
    7.37 -        state = sxp.child_value(dev_sxp, 'state')
    7.38 +        state = dev['state']
    7.39  
    7.40          if state == 'Initialising':
    7.41              # new create
    7.42 @@ -3237,6 +3241,9 @@ class XendDomainInfo:
    7.43      def get_dpcis(self):
    7.44          return XendDPCI.get_by_VM(self.info.get('uuid'))
    7.45  
    7.46 +    def get_dscsis(self):
    7.47 +        return XendDSCSI.get_by_VM(self.info.get('uuid'))
    7.48 +
    7.49      def create_vbd(self, xenapi_vbd, vdi_image_path):
    7.50          """Create a VBD using a VDI from XendStorageRepository.
    7.51  
    7.52 @@ -3417,6 +3424,60 @@ class XendDomainInfo:
    7.53  
    7.54          return dpci_uuid
    7.55  
    7.56 +    def create_dscsi(self, xenapi_dscsi):
    7.57 +        """Create scsi device from the passed struct in Xen API format.
    7.58 +
    7.59 +        @param xenapi_dscsi: DSCSI struct from Xen API
    7.60 +        @rtype: string
    7.61 +        @return: UUID
    7.62 +        """
    7.63 +
    7.64 +        dscsi_uuid = uuid.createString()
    7.65 +
    7.66 +        # Convert xenapi to sxp
    7.67 +        pscsi = XendAPIStore.get(xenapi_dscsi.get('PSCSI'), 'PSCSI')
    7.68 +        devid = int(xenapi_dscsi.get('virtual_HCTL').split(':')[0])
    7.69 +        target_vscsi_sxp = \
    7.70 +            ['vscsi', 
    7.71 +                ['dev',
    7.72 +                    ['devid', devid],
    7.73 +                    ['p-devname', pscsi.get_dev_name()],
    7.74 +                    ['p-dev', pscsi.get_physical_HCTL()],
    7.75 +                    ['v-dev', xenapi_dscsi.get('virtual_HCTL')],
    7.76 +                    ['state', 'Initialising'],
    7.77 +                    ['uuid', dscsi_uuid]
    7.78 +                ]
    7.79 +            ]
    7.80 +
    7.81 +        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
    7.82 +
    7.83 +            cur_vscsi_sxp = self._getDeviceInfo_vscsi(devid, None)
    7.84 +
    7.85 +            if cur_vscsi_sxp is None:
    7.86 +                dev_uuid = self.info.device_add('vscsi', cfg_sxp = target_vscsi_sxp)
    7.87 +                if not dev_uuid:
    7.88 +                    raise XendError('Failed to create device')
    7.89 +
    7.90 +            else:
    7.91 +                new_vscsi_sxp = ['vscsi']
    7.92 +                for existing_dev in sxp.children(cur_vscsi_sxp, 'dev'):
    7.93 +                    new_vscsi_sxp.append(existing_dev)
    7.94 +                new_vscsi_sxp.append(sxp.child0(target_vscsi_sxp, 'dev'))
    7.95 +
    7.96 +                dev_uuid = sxp.child_value(cur_vscsi_sxp, 'uuid')
    7.97 +                self.info.device_update(dev_uuid, new_vscsi_sxp)
    7.98 +
    7.99 +            xen.xend.XendDomain.instance().managed_config_save(self)
   7.100 +
   7.101 +        else:
   7.102 +            try:
   7.103 +                self.device_configure(target_vscsi_sxp)
   7.104 +
   7.105 +            except Exception, exn:
   7.106 +                raise XendError('Failed to create device')
   7.107 +
   7.108 +        return dscsi_uuid
   7.109 +
   7.110  
   7.111      def destroy_device_by_uuid(self, dev_type, dev_uuid):
   7.112          if dev_uuid not in self.info['devices']:
   7.113 @@ -3484,6 +3545,41 @@ class XendDomainInfo:
   7.114              except Exception, exn:
   7.115                  raise XendError('Failed to destroy device')
   7.116  
   7.117 +    def destroy_dscsi(self, dev_uuid):
   7.118 +        dscsi = XendAPIStore.get(dev_uuid, 'DSCSI')
   7.119 +        devid = dscsi.get_virtual_host()
   7.120 +        vHCTL = dscsi.get_virtual_HCTL()
   7.121 +        cur_vscsi_sxp = self._getDeviceInfo_vscsi(devid, None)
   7.122 +        dev_uuid = sxp.child_value(cur_vscsi_sxp, 'uuid')
   7.123 +
   7.124 +        target_dev = None
   7.125 +        new_vscsi_sxp = ['vscsi']
   7.126 +        for dev in sxp.children(cur_vscsi_sxp, 'dev'):
   7.127 +            if vHCTL == sxp.child_value(dev, 'v-dev'):
   7.128 +                target_dev = dev
   7.129 +            else:
   7.130 +                new_vscsi_sxp.append(dev)
   7.131 +
   7.132 +        if target_dev is None:
   7.133 +            raise XendError('Failed to destroy device')
   7.134 +
   7.135 +        target_dev.append(['state', 'Closing'])
   7.136 +        target_vscsi_sxp = ['vscsi', target_dev]
   7.137 +
   7.138 +        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
   7.139 +
   7.140 +            self.info.device_update(dev_uuid, new_vscsi_sxp)
   7.141 +            if len(sxp.children(new_vscsi_sxp, 'dev')) == 0:
   7.142 +                del self.info['devices'][dev_uuid]
   7.143 +            xen.xend.XendDomain.instance().managed_config_save(self)
   7.144 +
   7.145 +        else:
   7.146 +            try:
   7.147 +                self.device_configure(target_vscsi_sxp)
   7.148 +
   7.149 +            except Exception, exn:
   7.150 +                raise XendError('Failed to destroy device')
   7.151 +
   7.152      def destroy_xapi_instances(self):
   7.153          """Destroy Xen-API instances stored in XendAPIStore.
   7.154          """
   7.155 @@ -3508,6 +3604,10 @@ class XendDomainInfo:
   7.156          for dpci_uuid in XendDPCI.get_by_VM(self.info.get('uuid')):
   7.157              XendAPIStore.deregister(dpci_uuid, "DPCI")
   7.158              
   7.159 +        # Destroy DSCSI instances.
   7.160 +        for dscsi_uuid in XendDSCSI.get_by_VM(self.info.get('uuid')):
   7.161 +            XendAPIStore.deregister(dscsi_uuid, "DSCSI")
   7.162 +            
   7.163      def has_device(self, dev_class, dev_uuid):
   7.164          return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
   7.165  
     8.1 --- a/tools/python/xen/xend/XendNode.py	Wed Oct 15 15:58:09 2008 +0100
     8.2 +++ b/tools/python/xen/xend/XendNode.py	Thu Oct 16 09:50:18 2008 +0100
     8.3 @@ -22,6 +22,7 @@ import xen.lowlevel.xc
     8.4  
     8.5  from xen.util import Brctl
     8.6  from xen.util import pci as PciUtil
     8.7 +from xen.util import vscsi_util
     8.8  from xen.xend import XendAPIStore
     8.9  from xen.xend import osdep
    8.10  
    8.11 @@ -38,7 +39,8 @@ from XendNetwork import *
    8.12  from XendStateStore import XendStateStore
    8.13  from XendMonitor import XendMonitor
    8.14  from XendPPCI import XendPPCI
    8.15 -     
    8.16 +from XendPSCSI import XendPSCSI
    8.17 +
    8.18  class XendNode:
    8.19      """XendNode - Represents a Domain 0 Host."""
    8.20      
    8.21 @@ -53,6 +55,7 @@ class XendNode:
    8.22          * network
    8.23          * Storage Repository
    8.24          * PPCI
    8.25 +        * PSCSI
    8.26          """
    8.27          
    8.28          self.xc = xen.lowlevel.xc.xc()
    8.29 @@ -269,6 +272,24 @@ class XendNode:
    8.30              XendPPCI(ppci_uuid, ppci_record)
    8.31  
    8.32  
    8.33 +        # Initialise PSCSIs
    8.34 +        saved_pscsis = self.state_store.load_state('pscsi')
    8.35 +        saved_pscsi_table = {}
    8.36 +        if saved_pscsis:
    8.37 +            for pscsi_uuid, pscsi_record in saved_pscsis.items():
    8.38 +                try:
    8.39 +                    saved_pscsi_table[pscsi_record['scsi_id']] = pscsi_uuid
    8.40 +                except KeyError:
    8.41 +                    pass
    8.42 +
    8.43 +        for pscsi_record in vscsi_util.get_all_scsi_devices():
    8.44 +            if pscsi_record['scsi_id']:
    8.45 +                # If saved uuid exists, use it. Otherwise create one.
    8.46 +                pscsi_uuid = saved_pscsi_table.get(pscsi_record['scsi_id'],
    8.47 +                                                   uuid.createString())
    8.48 +                XendPSCSI(pscsi_uuid, pscsi_record)
    8.49 +
    8.50 +
    8.51  ##    def network_destroy(self, net_uuid):
    8.52   ##       del self.networks[net_uuid]
    8.53    ##      self.save_networks()
    8.54 @@ -320,6 +341,15 @@ class XendNode:
    8.55          return None
    8.56  
    8.57  
    8.58 +    def get_PSCSI_refs(self):
    8.59 +        return XendPSCSI.get_all()
    8.60 +
    8.61 +    def get_pscsi_by_uuid(self, pscsi_uuid):
    8.62 +        if pscsi_uuid in self.get_PSCSI_refs():
    8.63 +            return pscsi_uuid
    8.64 +        return None
    8.65 +
    8.66 +
    8.67      def save(self):
    8.68          # save state
    8.69          host_record = {self.uuid: {'name_label':self.name,
    8.70 @@ -333,6 +363,7 @@ class XendNode:
    8.71          self.save_PBDs()
    8.72          self.save_SRs()
    8.73          self.save_PPCIs()
    8.74 +        self.save_PSCSIs()
    8.75  
    8.76      def save_PIFs(self):
    8.77          pif_records = dict([(pif_uuid, XendAPIStore.get(
    8.78 @@ -363,6 +394,12 @@ class XendNode:
    8.79                              for ppci_uuid in XendPPCI.get_all()])
    8.80          self.state_store.save_state('ppci', ppci_records)
    8.81  
    8.82 +    def save_PSCSIs(self):
    8.83 +        pscsi_records = dict([(pscsi_uuid, XendAPIStore.get(
    8.84 +                                  pscsi_uuid, "PSCSI").get_record())
    8.85 +                            for pscsi_uuid in XendPSCSI.get_all()])
    8.86 +        self.state_store.save_state('pscsi', pscsi_records)
    8.87 +
    8.88      def shutdown(self):
    8.89          return 0
    8.90  
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tools/python/xen/xend/XendPSCSI.py	Thu Oct 16 09:50:18 2008 +0100
     9.3 @@ -0,0 +1,143 @@
     9.4 +#============================================================================
     9.5 +# This library is free software; you can redistribute it and/or
     9.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     9.7 +# License as published by the Free Software Foundation.
     9.8 +#
     9.9 +# This library is distributed in the hope that it will be useful,
    9.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    9.12 +# Lesser General Public License for more details.
    9.13 +#
    9.14 +# You should have received a copy of the GNU Lesser General Public
    9.15 +# License along with this library; if not, write to the Free Software
    9.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    9.17 +#============================================================================
    9.18 +# Copyright FUJITSU LIMITED 2008
    9.19 +#       Masaki Kanno <kanno.masaki@jp.fujitsu.com>
    9.20 +#============================================================================
    9.21 +
    9.22 +from xen.xend.XendBase import XendBase
    9.23 +from xen.xend.XendBase import XendAPIStore
    9.24 +from xen.xend import uuid as genuuid
    9.25 +
    9.26 +class XendPSCSI(XendBase):
    9.27 +    """Representation of a physical SCSI device."""
    9.28 +
    9.29 +    def getClass(self):
    9.30 +        return "PSCSI"
    9.31 +
    9.32 +    def getAttrRO(self):
    9.33 +        attrRO = ['host',
    9.34 +                  'physical_host',
    9.35 +                  'physical_channel',
    9.36 +                  'physical_target',
    9.37 +                  'physical_lun',
    9.38 +                  'physical_HCTL',
    9.39 +                  'vendor_name',
    9.40 +                  'model',
    9.41 +                  'type_id',
    9.42 +                  'type',
    9.43 +                  'dev_name',
    9.44 +                  'sg_name',
    9.45 +                  'revision',
    9.46 +                  'scsi_id',
    9.47 +                  'scsi_level']
    9.48 +        return XendBase.getAttrRO() + attrRO
    9.49 +
    9.50 +    def getAttrRW(self):
    9.51 +        attrRW = []
    9.52 +        return XendBase.getAttrRW() + attrRW
    9.53 +
    9.54 +    def getAttrInst(self):
    9.55 +        attrInst = []
    9.56 +        return XendBase.getAttrInst() + attrInst
    9.57 +
    9.58 +    def getMethods(self):
    9.59 +        methods = []
    9.60 +        return XendBase.getMethods() + methods
    9.61 +
    9.62 +    def getFuncs(self):
    9.63 +        funcs = []
    9.64 +        return XendBase.getFuncs() + funcs
    9.65 +
    9.66 +    getClass    = classmethod(getClass)
    9.67 +    getAttrRO   = classmethod(getAttrRO)
    9.68 +    getAttrRW   = classmethod(getAttrRW)
    9.69 +    getAttrInst = classmethod(getAttrInst)
    9.70 +    getMethods  = classmethod(getMethods)
    9.71 +    getFuncs    = classmethod(getFuncs)
    9.72 + 
    9.73 +    def get_by_HCTL(self, physical_HCTL):
    9.74 +        for pscsi in XendAPIStore.get_all("PSCSI"):
    9.75 +            if pscsi.get_physical_HCTL() == physical_HCTL:
    9.76 +                return pscsi.get_uuid()
    9.77 +        return None
    9.78 +
    9.79 +    get_by_HCTL = classmethod(get_by_HCTL)
    9.80 +
    9.81 +    def __init__(self, uuid, record):
    9.82 +        self.physical_HCTL = record['physical_HCTL']
    9.83 +        self.vendor_name = record['vendor_name']
    9.84 +        self.model = record['model']
    9.85 +        self.type_id = record['type_id']
    9.86 +        self.type = record['type']
    9.87 +        self.dev_name = record['dev_name']
    9.88 +        self.sg_name = record['sg_name']
    9.89 +        self.revision = record['revision']
    9.90 +        self.scsi_id = record['scsi_id']
    9.91 +        self.scsi_level = record['scsi_level']
    9.92 +
    9.93 +        p_hctl = self.physical_HCTL.split(':')
    9.94 +        self.physical_host = int(p_hctl[0])
    9.95 +        self.physical_channel = int(p_hctl[1])
    9.96 +        self.physical_target = int(p_hctl[2])
    9.97 +        self.physical_lun = int(p_hctl[3])
    9.98 +
    9.99 +        XendBase.__init__(self, uuid, record)
   9.100 +
   9.101 +    def get_host(self):
   9.102 +        from xen.xend import XendNode
   9.103 +        return XendNode.instance().get_uuid()
   9.104 +
   9.105 +    def get_physical_host(self):
   9.106 +        return self.physical_host
   9.107 +
   9.108 +    def get_physical_channel(self):
   9.109 +        return self.physical_channel
   9.110 +
   9.111 +    def get_physical_target(self):
   9.112 +        return self.physical_target
   9.113 +
   9.114 +    def get_physical_lun(self):
   9.115 +        return self.physical_lun
   9.116 +
   9.117 +    def get_physical_HCTL(self):
   9.118 +        return self.physical_HCTL
   9.119 +
   9.120 +    def get_vendor_name(self):
   9.121 +        return self.vendor_name
   9.122 +
   9.123 +    def get_model(self):
   9.124 +        return self.model
   9.125 +
   9.126 +    def get_type_id(self):
   9.127 +        return self.type_id
   9.128 +
   9.129 +    def get_type(self):
   9.130 +        return self.type
   9.131 +
   9.132 +    def get_dev_name(self):
   9.133 +        return self.dev_name
   9.134 +
   9.135 +    def get_sg_name(self):
   9.136 +        return self.sg_name
   9.137 +
   9.138 +    def get_revision(self):
   9.139 +        return self.revision
   9.140 +
   9.141 +    def get_scsi_id(self):
   9.142 +        return self.scsi_id
   9.143 +
   9.144 +    def get_scsi_level(self):
   9.145 +        return self.scsi_level
   9.146 +
    10.1 --- a/tools/python/xen/xend/server/vscsiif.py	Wed Oct 15 15:58:09 2008 +0100
    10.2 +++ b/tools/python/xen/xend/server/vscsiif.py	Thu Oct 16 09:50:18 2008 +0100
    10.3 @@ -125,10 +125,10 @@ class VSCSIController(DevController):
    10.4              state = self.readBackend(devid, devpath + '/state')
    10.5              localdevid = self.readBackend(devid, devpath + '/devid')
    10.6              dev_dict = {'p-dev': pdev,
    10.7 -                            'p-devname': pdevname,
    10.8 -                            'v-dev': pdevname,
    10.9 -                            'state': state,
   10.10 -                            'devid': localdevid }
   10.11 +                        'p-devname': pdevname,
   10.12 +                        'v-dev': vdev,
   10.13 +                        'state': state,
   10.14 +                        'devid': localdevid }
   10.15              vscsi_devs.append(dev_dict)
   10.16  
   10.17          config['devs'] = vscsi_devs
   10.18 @@ -168,17 +168,17 @@ class VSCSIController(DevController):
   10.19          (devid, back, front) = self.getDeviceDetails(config)
   10.20          devid = int(devid)
   10.21          vscsi_config = config['devs'][0]
   10.22 -        states = config.get('states', [])
   10.23 +        state = vscsi_config.get('state', '')
   10.24          driver_state = self.readBackend(devid, 'state')
   10.25          if str(xenbusState['Connected']) != driver_state:
   10.26              raise VmError("Driver status is not connected")
   10.27  
   10.28          uuid = self.readBackend(devid, 'uuid')
   10.29 -        if states[0] == 'Initialising':
   10.30 +        if state == 'Initialising':
   10.31              back['uuid'] = uuid
   10.32              self.writeBackend(devid, back)
   10.33  
   10.34 -        elif states[0] == 'Closing':
   10.35 +        elif state == 'Closing':
   10.36              found = False
   10.37              devs = self.readBackendList(devid, "vscsi-devs")
   10.38              vscsipath = "vscsi-devs/"
   10.39 @@ -197,8 +197,8 @@ class VSCSIController(DevController):
   10.40                  raise VmError("Device %s not connected" % vdev)
   10.41  
   10.42          else:
   10.43 -            raise XendError('Error configuring device invalid state %s'
   10.44 -                                % state)
   10.45 +            raise XendError("Error configuring device invalid "
   10.46 +                            "state '%s'" % state)
   10.47  
   10.48          self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring']))
   10.49          return self.readBackend(devid, 'uuid')
    11.1 --- a/tools/python/xen/xm/create.dtd	Wed Oct 15 15:58:09 2008 +0100
    11.2 +++ b/tools/python/xen/xm/create.dtd	Thu Oct 16 09:50:18 2008 +0100
    11.3 @@ -40,6 +40,7 @@
    11.4                   vif*,
    11.5                   vtpm*,
    11.6                   pci*,
    11.7 +                 vscsi*,
    11.8                   console*,
    11.9                   platform*,
   11.10                   vcpu_param*,
   11.11 @@ -88,6 +89,10 @@
   11.12                   func            CDATA #REQUIRED
   11.13                   vslt            CDATA #IMPLIED>
   11.14  
   11.15 +<!ELEMENT vscsi  EMPTY>
   11.16 +<!ATTLIST vscsi  p-dev           CDATA #REQUIRED
   11.17 +                 v-dev           CDATA #REQUIRED>
   11.18 +
   11.19  <!ELEMENT console (other_config*)>
   11.20  <!ATTLIST console protocol       (vt100|rfb|rdp) #REQUIRED>
   11.21  
    12.1 --- a/tools/python/xen/xm/main.py	Wed Oct 15 15:58:09 2008 +0100
    12.2 +++ b/tools/python/xen/xm/main.py	Thu Oct 16 09:50:18 2008 +0100
    12.3 @@ -2235,12 +2235,34 @@ def vscsi_convert_sxp_to_dict(dev_sxp):
    12.4      return dev_dict
    12.5  
    12.6  def xm_scsi_list(args):
    12.7 -    xenapi_unsupported()
    12.8      (use_long, params) = arg_check_for_resource_list(args, "scsi-list")
    12.9  
   12.10      dom = params[0]
   12.11  
   12.12 -    devs = server.xend.domain.getDeviceSxprs(dom, 'vscsi')
   12.13 +    devs = []
   12.14 +    if serverType == SERVER_XEN_API:
   12.15 +
   12.16 +        dscsi_refs = server.xenapi.VM.get_DSCSIs(get_single_vm(dom))
   12.17 +        dscsi_properties = \
   12.18 +            map(server.xenapi.DSCSI.get_runtime_properties, dscsi_refs)
   12.19 +        dscsi_dict = {}
   12.20 +        for dscsi_property in dscsi_properties:
   12.21 +            devid = int(dscsi_property['dev']['devid'])
   12.22 +            try:
   12.23 +                dscsi_sxp = dscsi_dict[devid]
   12.24 +            except:
   12.25 +                dscsi_sxp = [['devs', []]]
   12.26 +                for key, value in dscsi_property.items():
   12.27 +                    if key != 'dev':
   12.28 +                        dscsi_sxp.append([key, value])
   12.29 +            dev_sxp = ['dev']
   12.30 +            dev_sxp.extend(map2sxp(dscsi_property['dev']))
   12.31 +            dscsi_sxp[0][1].append(dev_sxp)
   12.32 +            dscsi_dict[devid] = dscsi_sxp
   12.33 +        devs = map2sxp(dscsi_dict)
   12.34 +
   12.35 +    else:
   12.36 +        devs = server.xend.domain.getDeviceSxprs(dom, 'vscsi')
   12.37  
   12.38      if use_long:
   12.39          map(PrettyPrint.prettyprint, devs)
   12.40 @@ -2464,37 +2486,60 @@ def xm_pci_attach(args):
   12.41      else:
   12.42          server.xend.domain.device_configure(dom, pci)
   12.43  
   12.44 +def parse_scsi_configuration(p_scsi, v_hctl, state):
   12.45 +    v = v_hctl.split(':')
   12.46 +    if len(v) != 4:
   12.47 +        raise OptionError("Invalid argument: %s" % v_hctl)
   12.48 +
   12.49 +    if p_scsi is not None:
   12.50 +        (p_hctl, block) = vscsi_util.vscsi_search_hctl_and_block(p_scsi)
   12.51 +        if p_hctl == None:
   12.52 +            raise OptionError("Cannot find device '%s'" % p_scsi)
   12.53 +    else:
   12.54 +        p_hctl = ''
   12.55 +        block = ''
   12.56 +
   12.57 +    scsi = ['vscsi']
   12.58 +    scsi.append(['dev', \
   12.59 +                 ['state', state], \
   12.60 +                 ['devid', int(v[0])], \
   12.61 +                 ['p-dev', p_hctl], \
   12.62 +                 ['p-devname', block], \
   12.63 +                 ['v-dev', v_hctl] \
   12.64 +               ])
   12.65 +
   12.66 +    return scsi
   12.67 +
   12.68  def xm_scsi_attach(args):
   12.69 -    xenapi_unsupported()
   12.70 -
   12.71      arg_check(args, 'scsi-attach', 3, 4)
   12.72 -    p_devname = args[1]
   12.73 -    v_dev = args[2]
   12.74 -
   12.75 -    v_hctl = v_dev.split(':')
   12.76 -    if len(v_hctl) != 4:
   12.77 -        raise OptionError("Invalid argument: %s" % v_dev)
   12.78 -
   12.79 -    (p_hctl, block) = vscsi_util.vscsi_search_hctl_and_block(p_devname)
   12.80 -
   12.81 -    if p_hctl == None:
   12.82 -        raise OptionError("Cannot find device \"%s\"" % p_devname)
   12.83 -
   12.84      dom = args[0]
   12.85 -    vscsi = ['vscsi']
   12.86 -    vscsi.append(['dev', \
   12.87 -                ['state', 'Initialising'], \
   12.88 -                ['devid', v_hctl[0]], \
   12.89 -                ['p-dev', p_hctl], \
   12.90 -                ['p-devname', block], \
   12.91 -                ['v-dev', v_dev] ])
   12.92 -
   12.93 -    if len(args) == 4:
   12.94 -        vscsi.append(['backend', args[3]])
   12.95 -
   12.96 -    vscsi.append(['state', 'Initialising'])
   12.97 -    vscsi.append(['devid', v_hctl[0]])
   12.98 -    server.xend.domain.device_configure(dom, vscsi)
   12.99 +    p_scsi = args[1]
  12.100 +    v_hctl = args[2]
  12.101 +    scsi = parse_scsi_configuration(p_scsi, v_hctl, 'Initialising')
  12.102 +
  12.103 +    if serverType == SERVER_XEN_API:
  12.104 +
  12.105 +        scsi_dev = sxp.children(scsi, 'dev')[0]
  12.106 +        p_hctl = sxp.child_value(scsi_dev, 'p-dev')
  12.107 +        target_ref = None
  12.108 +        for pscsi_ref in server.xenapi.PSCSI.get_all():
  12.109 +            if p_hctl == server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
  12.110 +                target_ref = pscsi_ref
  12.111 +                break
  12.112 +        if target_ref is None:
  12.113 +            raise OptionError("Cannot find device '%s'" % p_scsi)
  12.114 +
  12.115 +        dscsi_record = {
  12.116 +            "VM":           get_single_vm(dom),
  12.117 +            "PSCSI":        target_ref,
  12.118 +            "virtual_HCTL": v_hctl
  12.119 +        }
  12.120 +        server.xenapi.DSCSI.create(dscsi_record)
  12.121 +
  12.122 +    else:
  12.123 +        if len(args) == 4:
  12.124 +            scsi.append(['backend', args[3]])
  12.125 +        server.xend.domain.device_configure(dom, scsi)
  12.126  
  12.127  def detach(args, deviceClass):
  12.128      rm_cfg = True
  12.129 @@ -2587,26 +2632,25 @@ def xm_pci_detach(args):
  12.130          server.xend.domain.device_configure(dom, pci)
  12.131  
  12.132  def xm_scsi_detach(args):
  12.133 -    xenapi_unsupported()
  12.134      arg_check(args, 'scsi-detach', 2)
  12.135 -
  12.136 -    v_dev = args[1]
  12.137 -    v_hctl = v_dev.split(':')
  12.138 -    if len(v_hctl) != 4:
  12.139 -        raise OptionError("Invalid argument: %s" % v_dev)
  12.140 -
  12.141      dom = args[0]
  12.142 -    vscsi = ['vscsi']
  12.143 -    vscsi.append(['dev', \
  12.144 -                ['state', 'Closing'], \
  12.145 -                ['devid', v_hctl[0]], \
  12.146 -                ['p-dev', ''], \
  12.147 -                ['p-devname', ''], \
  12.148 -                ['v-dev', v_dev] ])
  12.149 -
  12.150 -    vscsi.append(['state', 'Closing'])
  12.151 -    vscsi.append(['devid', v_hctl[0]])
  12.152 -    server.xend.domain.device_configure(dom, vscsi)
  12.153 +    v_hctl = args[1]
  12.154 +    scsi = parse_scsi_configuration(None, v_hctl, 'Closing')
  12.155 +
  12.156 +    if serverType == SERVER_XEN_API:
  12.157 +
  12.158 +        target_ref = None
  12.159 +        for dscsi_ref in server.xenapi.VM.get_DSCSIs(get_single_vm(dom)):
  12.160 +            if v_hctl == server.xenapi.DSCSI.get_virtual_HCTL(dscsi_ref):
  12.161 +                target_ref = dscsi_ref
  12.162 +                break
  12.163 +        if target_ref is None:
  12.164 +            raise OptionError("Device %s not assigned" % v_hctl)
  12.165 +
  12.166 +        server.xenapi.DSCSI.destroy(target_ref)
  12.167 +
  12.168 +    else:
  12.169 +        server.xend.domain.device_configure(dom, scsi)
  12.170  
  12.171  def xm_vnet_list(args):
  12.172      xenapi_unsupported()
    13.1 --- a/tools/python/xen/xm/xenapi_create.py	Wed Oct 15 15:58:09 2008 +0100
    13.2 +++ b/tools/python/xen/xm/xenapi_create.py	Thu Oct 16 09:50:18 2008 +0100
    13.3 @@ -375,6 +375,12 @@ class xenapi_create:
    13.4  
    13.5              self.create_pcis(vm_ref, pcis)
    13.6  
    13.7 +            # Now create scsis
    13.8 +
    13.9 +            scsis = vm.getElementsByTagName("vscsi")
   13.10 +
   13.11 +            self.create_scsis(vm_ref, scsis)
   13.12 +
   13.13              return vm_ref
   13.14          except:
   13.15              server.xenapi.VM.destroy(vm_ref)
   13.16 @@ -532,6 +538,33 @@ class xenapi_create:
   13.17  
   13.18          return server.xenapi.DPCI.create(dpci_record)
   13.19  
   13.20 +    def create_scsis(self, vm_ref, scsis):
   13.21 +        log(DEBUG, "create_scsis")
   13.22 +        return map(lambda scsi: self.create_scsi(vm_ref, scsi), scsis)
   13.23 +
   13.24 +    def create_scsi(self, vm_ref, scsi):
   13.25 +        log(DEBUG, "create_scsi")
   13.26 +
   13.27 +        target_ref = None
   13.28 +        for pscsi_ref in server.xenapi.PSCSI.get_all():
   13.29 +            if scsi.attributes["p-dev"].value == server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
   13.30 +                target_ref = pscsi_ref
   13.31 +                break
   13.32 +        if target_ref is None:
   13.33 +            log(DEBUG, "create_scsi: scsi device not found")
   13.34 +            return None
   13.35 +
   13.36 +        dscsi_record = {
   13.37 +            "VM":
   13.38 +                vm_ref,
   13.39 +            "PSCSI":
   13.40 +                target_ref,
   13.41 +            "virtual_HCTL":
   13.42 +                scsi.attributes["v-dev"].value
   13.43 +        }
   13.44 +
   13.45 +        return server.xenapi.DSCSI.create(dscsi_record)
   13.46 +
   13.47  def get_child_by_name(exp, childname, default = None):
   13.48      try:
   13.49          return [child for child in sxp.children(exp)
   13.50 @@ -563,6 +596,9 @@ class sxp2xml:
   13.51          pcis_sxp = map(lambda x: x[1], [device for device in devices
   13.52                                          if device[1][0] == "pci"])
   13.53  
   13.54 +        scsis_sxp = map(lambda x: x[1], [device for device in devices
   13.55 +                                         if device[1][0] == "vscsi"])
   13.56 +
   13.57          # Create XML Document
   13.58          
   13.59          impl = getDOMImplementation()
   13.60 @@ -704,6 +740,12 @@ class sxp2xml:
   13.61  
   13.62          map(vm.appendChild, pcis)
   13.63  
   13.64 +        # And now the scsis
   13.65 +
   13.66 +        scsis = self.extract_scsis(scsis_sxp, document)
   13.67 +
   13.68 +        map(vm.appendChild, scsis)
   13.69 +
   13.70          # Last but not least the consoles...
   13.71  
   13.72          consoles = self.extract_consoles(image, document)
   13.73 @@ -894,6 +936,23 @@ class sxp2xml:
   13.74  
   13.75          return pcis
   13.76  
   13.77 +    def extract_scsis(self, scsis_sxp, document):
   13.78 +
   13.79 +        scsis = []
   13.80 +
   13.81 +        for scsi_sxp in scsis_sxp:
   13.82 +            for dev_sxp in sxp.children(scsi_sxp, "dev"):
   13.83 +                scsi = document.createElement("vscsi")
   13.84 +
   13.85 +                scsi.attributes["p-dev"] \
   13.86 +                    = get_child_by_name(dev_sxp, "p-dev")
   13.87 +                scsi.attributes["v-dev"] \
   13.88 +                    = get_child_by_name(dev_sxp, "v-dev")
   13.89 +
   13.90 +                scsis.append(scsi)
   13.91 +
   13.92 +        return scsis
   13.93 +
   13.94      def mk_other_config(self, key, value, document):
   13.95          other_config = document.createElement("other_config")
   13.96          other_config.attributes["key"] = key