LUN-per-vdi SR type
===================

The LUN-per-vdi driver presents an unusual SR type since it relies
on external device configuration to define the VDI contents of the
SR. Fortunately, in the rio model, we have the ability to
share SR data via the pool, so an SR only needs to be configured once
for multiple hosts to access VDI metadata. The general concepts are:

a) LUNs are allocated externally on an iSCSI target for inclusion in
a LUN-per-vdi SR

b) LUN access mapping must also be configured on the target for all
hosts in the pool. i.e. a list of initiator IQNs must be compiled in
advance and enabled on the target

c) Controlling access and locking of VDIs must be handled by the master

d) Snapshot/clone operations are not supported via the api interface and
should be handled externally using the target management software

e) All LUNs are uniquely identified by a disk serial number which is used
to identify the same LUN across hosts in the pool.

f) A determistic mapping between a serial number and a UUID string can
be applied to uniquely identify a disk

Due to the significant difference in discovery and access of LUNs between
FC/iSCSI hardware and software-based initiators it is recommended that
different driver types should be developed for each. Therefore, with the
current supported compatibility list, a separate driver type would be 
required for:

1] software iSCSI
2] Emulex HBA
3] Qlogic HBA
4] Qlogic iSCSI

Functionality would be very similar between the drivers, only requiring
custom commands to query devices and parse output. 

***N.B. In this manner, each access type would constitute a different SR, 
even though the LUNs may be hosted on the same target device. Using the
determinstic serial number to UUID mapping should ensure devices are 
uniquely referenced, and prevent conflicts.***


Generating a UUID based on the disk serial number
=================================================

The length of an iscsi disk serial number appears to be a standard length
of 12 characters [*]. Disk serial numbers are the preferred method of
identifying disks using a human readable string. By prepending a fixed 
length string generated from the target IQN number to the beginning of 
the serial number and converting the whole string to hex, a deterministic 
uuid to serial string mapping can be applied. 
(see end of document: gen_uuid_from_serial(),gen_serial_from_uuid()
for an initial stab at this functionality)

[*] Requires further research + testing against supported targets.
Initially we would fail if serial number was not present or incorrect
length.


Software-Based Driver Operation
===============================

Based on the previous observations, the SR operations can be implemented 
in the following manner:

DCONF parameters:
      - Target IQN
      - Target IP
      - Target Port number
      - Host UUID to InitiatorIQN mapping
      - CHAP username
      - CHAP password
      - ... (other paramaters likely to be identified)

'sr_create' - Unsupported operation
'sr_delete' - Unsupported operation
'sr_attach' - Using the dconf parameters specified above, the host can
	      initiate an iSCSI session and attach mapped LUNs. Available
	      LUNs can then be 'scanned' on-the-fly by listing the contents
	      of /dev/iscsi/<TARGET IQN>/LUN* and applying the serial number
	      to UUID mapper.
'sr_detach' - The software initiator connection is disabled and LUNs unmapped. 
'sr_scan'   - As outlined above, the contents of the IQN directory indicate
	      the available LUNs. The UUID is generated by querying each disk
	      serial number. 
'vdi_create'- Unsupported operation
'vdi_delete'- Unsupported operation
'vdi_attach'- Verify the VDI is accessible. All the attach path should already
	      be initialised during sr_attach
'vdi_detach'- No-op
'vdi_clone' - Unsupported operation
'vdi_snapshot'- Unsupported operation
'vdi_resize'- Unsupported operation
'vdi_lock'  - Unsupported operation
'vdi_unlock'- Unsupported operation


Hardware-Based FC/iSCSI Driver Operations
=========================================

Based on the previous observations, the SR operations can be implemented 
in the following manner:

DCONF parameters are specific to the hardware type, however in general
all relevant session connection parameters are passed via the dconf to the 
driver.

'sr_create' - Unsupported operation
'sr_delete' - Unsupported operation
'sr_attach' - Using the dconf parameters specified above, the host can
	      initiate an FC session and attach mapped LUNs. Available
	      LUNs can then be 'scanned' on-the-fly by querying the hardware
	      controller utility. For every available device it should
	      be feasible to retrieve the serial number and map the UUID.
'sr_detach' - Using the hardware controller utility, the connection is 
	      disabled and LUNs unmapped. 
'sr_scan'   - As outlined above, by querying the device it is possible to
	      list all mapped LUNs.
'vdi_create'- Unsupported operation
'vdi_delete'- Unsupported oepration
'vdi_attach'- Verify the VDI is accessible. All the attach path should already
	      be initialised during sr_attach
'vdi_detach'- No-op
'vdi_clone' - Unsupported operation
'vdi_snapshot'- Unsupported operation
'vdi_resize'- Unsupported operation
'vdi_lock'  - Unsupported operation
'vdi_unlock'- Unsupported operation


Configuring an XE host Cluster to use LUN-per-VDI
=================================================

In order to associate an externally configured LUN with the XE pool, the
admin must, at some stage, manually enter data and insert useful tags 
against a VDI (LUN). An example here might be a LUN that
contains a customised RHEL 4.4 image used for gold mastering by the admin.
Once the LUN has been introduced to the pool via an sr_attach and subsequent
scan, the admin would want to tag that image as a RHEL gold master. By making 
use of the 'other-config' and possibly 'vdi-name' fields provided by the pool 
master, metadata can be stored centrally and accessed by any member of 
the pool. The typical user-identifiable string is the LUN serial number 
which is normally auto-configured by the target.

The process for introducing an externally created SR to the XE system 
would therefore look something like:

1) Filer Admin configures filer and allocates raw LUNs to an access group. 
At this stage, all we have is a serial number and LUN bus id to uniquely 
identify that LUN to a host. There is no other way of *generically* passing 
other metadata about that LUN to any host.

2) XE admin creates a new instance of an iscsi-based SR on the master with 
the correct parameters such as target IP address, host IQN, CHAP settings etc... 
This enables the host to attach the available LUNs on the target with each 
LUN appearing as a device under /dev/iscsi/<TARGET IQN>/LUN# where the 
number corresponds to the bus ID.

3) The scanning thread begins querying the driver for available VDIs. 
The driver scans the device directory, and for each entry it queries the 
LUN serial number and applies the UUID mapping. All VDIs then appear in 
the host agent database with the unique UUID.

4) The XE admin now has the option to manually enter metadata relating to 
each LUN (it would be good to also return the serial number in the scanning 
thread so that the serial number to UUID mapping is already stored with the 
VDI on the host and visible to the user). 

5) Attaching the SR to other members of the pool is straightforward from this
point. Each host can pull out it's own IQN initiator number from the PBD 
record and attach the target LUNs. Scanning the LUNs should only be handled
by the master to avoid multiple hosts accessing the devices.


Locking
=======

In the first instance, all locking and access control to LUNs must be handled
centrally by the pool master. In version 2, we may want to consider looking at 
SCSI persistent reservations to handle locking, but not in the rio timeframe.


Suggested hash + UUID/Serial Mapping Code
=========================================

PREFIX_LEN = 4

def hash_of_iqn(iqn, len):
    hs = 0
    for i in iqn:
        hs = ord(i) + (hs << 6) + (hs << 16) - hs;
    ret = "%d" % (hs >> PREFIX_LEN)
    return ret[0:PREFIX_LEN]

def gen_uuid_from_serial(iqn, serial):
    #Assert string >= 12 chars
    if len(serial) < 12:
        raise CommandException(1)
    prefix = hash_of_iqn(iqn, PREFIX_LEN)
    str = prefix.encode("hex") + serial[0:12].encode("hex")
    return str[0:8]+'-'+str[8:12]+'-'+str[12:16]+'-'+str[16:20]+'-'+str[20:32]

def gen_serial_from_uuid(iqn, uuid):
    str = uuid.replace('-','')
    prefix = hash_of_iqn(iqn, PREFIX_LEN)
    if str[0:(PREFIX_LEN * 2)].decode("hex") != prefix:
        raise CommandException(1)
    return str[(PREFIX_LEN * 2):].decode("hex")
