├── src ├── xapi │ ├── __init__.py │ └── storage │ │ ├── __init__.py │ │ └── libs │ │ ├── __init__.py │ │ └── xcpng │ │ ├── __init__.py │ │ └── librbd │ │ ├── __init__.py │ │ ├── datapath.py │ │ ├── locks.py │ │ ├── meta.py │ │ ├── volume.py │ │ ├── sr.py │ │ └── rbd_utils.py ├── datapath │ └── rbd+qcow2+qdisk │ │ └── plugin.json └── volume │ └── org.xen.xapi.storage.rbdsr │ └── plugin.json ├── .gitattributes ├── netinstall.sh ├── .gitignore ├── README.md ├── rpm └── RBDSR.spec ├── install └── RBDSR.sh └── LICENSE /src/xapi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/xapi/storage/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /netinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | REPO="https://github.com/rposudnevskiy" 3 | PROJECT="RBDSR" 4 | BRANCH="3.0" 5 | 6 | #Install xcpng-storage-libs 7 | sh <(curl -s https://raw.githubusercontent.com/rposudnevskiy/xcpng-storage-libs/master/netinstall.sh) 8 | 9 | cd ~ 10 | wget -q "$REPO/$PROJECT/archive/v$BRANCH.zip" -O ~/$PROJECT-v$BRANCH.zip 11 | unzip -qq -o ~/$PROJECT-v$BRANCH.zip -d ~ 12 | cd ~/$PROJECT-$BRANCH 13 | 14 | sh ./install/$PROJECT.sh install $1 15 | -------------------------------------------------------------------------------- /src/datapath/rbd+qcow2+qdisk/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugin": "rbd+qcow2+qdisk", 3 | "name": "The RBD QEMU qdisk user-space datapath plugin", 4 | "description": "This plugin manages and configures qdisk instances backend for rbd image in QCOW2 format built using librbd", 5 | "vendor": "Roman V. Posudnevskiy", 6 | "copyright": "(c) 2019 Roman V. Posudnevskiy", 7 | "version": "3.0", 8 | "required_api_version": "5.0", 9 | "features": [], 10 | "configuration": {}, 11 | "required_cluster_stack": [] 12 | } -------------------------------------------------------------------------------- /src/volume/org.xen.xapi.storage.rbdsr/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugin": "rbdsr", 3 | "name": "RBDSR Volume plugin", 4 | "description": "This plugin creates a SR on a CEPH pool", 5 | "vendor": "Roman V. Posudnevskiy", 6 | "copyright": "(c) 2019 Roman V. Posudnevskiy", 7 | "version": "3.0", 8 | "required_api_version": "5.0", 9 | "features": [ 10 | "SR_ATTACH", 11 | "SR_DETACH", 12 | "SR_CREATE", 13 | "VDI_CREATE", 14 | "VDI_DESTROY", 15 | "VDI_ATTACH", 16 | "VDI_ATTACH_OFFLINE", 17 | "VDI_DETACH", 18 | "VDI_ACTIVATE", 19 | "VDI_DEACTIVATE", 20 | "VDI_UPDATE", 21 | "VDI_CLONE", 22 | "VDI_SNAPSHOT", 23 | "VDI_RESIZE", 24 | "VDI_RESIZE_ONLINE", 25 | "SR_METADATA"], 26 | "configuration": {}, 27 | "required_cluster_stack": [] 28 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | .project 49 | .idea 50 | *.komodoproject 51 | .komodotools -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RBDSR - XenServer 7.5 / XCP-ng 7.5 Storage Manager plugin for CEPH (v3.0) for SMAPIv3 2 | This plugin adds support for Ceph block devices into XenServer / XCP-ng. 3 | It supports the creation of VDI as RBD device in Ceph pool. 4 | 5 | Please note that `v1.0` and `v2.0` and `v3.0` are not compatible. At the moment there is no mean to migrate between versions. Hope to implement it later. 6 | 7 | Plugin requires `qemu-dp.x86_64` ([xcp-ng-rpms/qemu-dp](https://github.com/xcp-ng-rpms/qemu-dp)), `python-rbd.x86_64` and `rbd-nbd.x86_64` packages installed 8 | 9 | ## Installation 10 | 11 | 1. Run this command: 12 | 13 | # bash <(curl -s https://raw.githubusercontent.com/rposudnevskiy/RBDSR/v3.0/netinstall.sh) 14 | 15 | To install certain version of ceph storage, provide version name (```jewel```, ```luminous``` or ```mimic```) as first parameter: 16 | 17 | # bash <(curl -s https://raw.githubusercontent.com/rposudnevskiy/RBDSR/v3.0/netinstall.sh) mimic 18 | 19 | 2. Create ```/etc/ceph/ceph.conf``` accordingly you Ceph cluster. The easiest way is just copy it from your Ceph cluster node 20 | 21 | 3. Copy ```/etc/ceph/ceph.client.admin.keyring``` to XenServer / XCP-ng hosts from your Ceph cluster node. 22 | 23 | 4. Restart Xapi storage script plugin server on XenServer / XCP-ng hosts 24 | 25 | # systemctl restart xapi-storage-script.service 26 | 27 | ## Removal 28 | 1. Remove all Ceph RBD SR out of XenServer / XCP-ng with the appropriate commands. 29 | 30 | 2. Run this command: 31 | 32 | # ~/RDBSRv3.0/install.sh deinstall 33 | 34 | 3. Restart Xapi storage script plugin server on XenServer / XCP-ng hosts 35 | 36 | # systemctl restart xapi-storage-script.service 37 | 38 | 39 | ## Usage 40 | 41 | Create a pool: 42 | 43 | # xe sr-create host-uuid=fb0d42fc-0a4d-459d-8b90-6ed6610c2e4c name-label="CEPH RBD Storage" shared=true type=rbdsr content-type=user device-config:cluster=ceph device-config:image-format=qcow2 device-config:datapath=qdisk 44 | 45 | 46 | The SR should be connected to the XenServer / XCP-ng hosts and be visible in XenCenter. 47 | / XCP-ng Center 48 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/datapath.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os.path import exists 4 | from xapi.storage.libs.xcpng.datapath import DatapathOperations as _DatapathOperations_ 5 | from xapi.storage.libs.xcpng.meta import IMAGE_UUID_TAG 6 | from xapi.storage.libs.xcpng.utils import call, _call, get_cluster_name_by_uri, get_sr_name_by_uri, VDI_PREFIXES, \ 7 | get_vdi_type_by_uri 8 | 9 | from xapi.storage import log 10 | 11 | NBDS_MAX = 32 12 | 13 | class DatapathOperations(_DatapathOperations_): 14 | 15 | def _is_nbd_device_connected(self, dbg, nbd_device): 16 | call(dbg, ['/usr/sbin/modprobe', 'nbd', "nbds_max=%s" % NBDS_MAX]) 17 | if not exists(nbd_device): 18 | raise Exception('There are no more free NBD devices') 19 | returncode = _call(dbg, ['/usr/sbin/nbd-client', '-check', nbd_device]) 20 | if returncode == 0: 21 | return True 22 | if returncode == 1: 23 | return False 24 | 25 | def _find_unused_nbd_device(self, dbg): 26 | for device_no in range(0, NBDS_MAX): 27 | nbd_device = "/dev/nbd{}".format(device_no) 28 | if not self._is_nbd_device_connected(dbg, nbd_device=nbd_device): 29 | return nbd_device 30 | 31 | def map_vol(self, dbg, uri, chained=False): 32 | if chained is False: 33 | log.debug("%s: xcpng.librbd.datapath.QdiskDatapath.map_vol: uri: %s" % (dbg, uri)) 34 | nbd_dev = self._find_unused_nbd_device(dbg) 35 | call(dbg, ['/usr/lib64/qemu-dp-xcpng/bin/qemu-nbd', 36 | '-c', nbd_dev, 37 | '-f', 'raw', 38 | self.gen_vol_uri(dbg, uri)]) 39 | volume_meta = {'nbd_dev': nbd_dev} 40 | self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) 41 | self.blkdev = nbd_dev 42 | super(DatapathOperations, self).map_vol(dbg, uri, chained=False) 43 | 44 | def unmap_vol(self, dbg, uri, chained=False): 45 | if chained is False: 46 | log.debug("%s: xcpng.librbd.datapath.QdiskDatapath.unmap_vol: uri: %s" % (dbg, uri)) 47 | super(DatapathOperations, self).unmap_vol(dbg, uri, chained=False) 48 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 49 | call(dbg, ['/usr/lib64/qemu-dp-xcpng/bin/qemu-nbd', '-d', volume_meta['nbd_dev']]) 50 | volume_meta = {'nbd_dev': None} 51 | self.MetadataHandler.update_vdi_meta(dbg, uri, volume_meta) 52 | 53 | def gen_vol_uri(self, dbg, uri): 54 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 55 | return "rbd:%s/%s%s:conf=/etc/ceph/%s.conf" % (get_sr_name_by_uri(dbg, uri), 56 | VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)], 57 | volume_meta[IMAGE_UUID_TAG], 58 | get_cluster_name_by_uri(dbg, uri)) -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/locks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import traceback 4 | 5 | from time import time, sleep 6 | from xapi.storage.libs.xcpng.meta import LocksOpsMgr as _LocksOpsMgr_ 7 | from xapi.storage.libs.xcpng.librbd.rbd_utils import ceph_cluster, rbd_lock, rbd_unlock, ImageBusy, ImageExists, \ 8 | is_locked 9 | from xapi.storage.libs.xcpng.utils import get_sr_uuid_by_uri, get_vdi_uuid_by_uri, get_cluster_name_by_uri, \ 10 | get_sr_name_by_uri, get_vdi_name_by_uri 11 | 12 | class LocksOpsMgr(_LocksOpsMgr_): 13 | 14 | def lock(self, dbg, uri, timeout=10): 15 | log.debug("%s: xcpng.librbd.meta.MetaDBOpeations.lock: uri: %s timeout: %s" % (dbg, uri, timeout)) 16 | 17 | sr_uuid = get_sr_uuid_by_uri(dbg, uri) 18 | vdi_uuid = get_vdi_uuid_by_uri(dbg, uri) 19 | pool_name = get_sr_name_by_uri(dbg, uri) 20 | 21 | if vdi_uuid is not None: 22 | lock_uuid = vdi_uuid 23 | image_name = get_vdi_name_by_uri(dbg, uri) 24 | else: 25 | lock_uuid = sr_uuid 26 | image_name = '__lock__' 27 | 28 | start_time = time() 29 | 30 | lh = [None, None, None] 31 | lh[0] = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 32 | 33 | if is_locked(dbg, lh[0], pool_name, '__lock__'): 34 | # SR is locked 35 | raise ImageBusy 36 | 37 | try: 38 | while True: 39 | try: 40 | if lock_uuid in self.__lhs: 41 | raise ImageExists 42 | 43 | lh[0].connect() 44 | lh[1], lh[2] = rbd_lock(dbg, 45 | lh[0], 46 | pool_name, 47 | image_name) 48 | self.__lhs[lock_uuid] = lh 49 | break 50 | except Exception as e: 51 | if time() - start_time >= timeout: 52 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.lock: Failed to lock: uri: %s" % (dbg, uri)) 53 | raise Exception(e) 54 | sleep(1) 55 | pass 56 | except Exception as e: 57 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.lock: Failed to lock: uri: %s" 58 | % (dbg, uri)) 59 | log.error(traceback.format_exc()) 60 | lh[0].shutdown() 61 | raise Exception(e) 62 | 63 | def unlock(self, dbg, uri): 64 | log.debug("%s: xcpng.librbd.meta.MetaDBOpeations.unlock: uri: %s" % (dbg, uri)) 65 | 66 | sr_uuid = get_sr_uuid_by_uri(dbg, uri) 67 | vdi_uuid = get_vdi_uuid_by_uri(dbg, uri) 68 | 69 | if vdi_uuid is not None: 70 | lock_uuid = vdi_uuid 71 | else: 72 | lock_uuid = sr_uuid 73 | 74 | if lock_uuid in self.__lhs: 75 | lh = self.__lhs[lock_uuid] 76 | rbd_unlock(dbg, lh) 77 | lh[0].shutdown() 78 | del self.__lhs[lock_uuid] -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/meta.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import traceback 4 | 5 | from struct import pack, unpack 6 | from xapi.storage import log 7 | from xapi.storage.libs.xcpng.meta import MetaDBOperations as _MetaDBOperations_ 8 | from xapi.storage.libs.xcpng.utils import get_sr_name_by_uri, get_cluster_name_by_uri 9 | from xapi.storage.libs.xcpng.librbd.rbd_utils import ceph_cluster, rbd_create, rbd_write, rbd_read, rbd_remove, \ 10 | rbd_lock, rbd_unlock 11 | 12 | CEPH_CLUSTER_TAG = 'cluster' 13 | 14 | 15 | class MetaDBOperations(_MetaDBOperations_): 16 | 17 | def __init__(self): 18 | self.lh = None 19 | 20 | def create(self, dbg, uri, db, size=8388608): 21 | log.debug("%s: xcpng.librbd.meta.MetaDBOpeations.create: uri: %s" % (dbg, uri)) 22 | 23 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 24 | 25 | try: 26 | cluster.connect() 27 | rbd_create(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__meta__', size) # default size = 8388608 = 8Mb 28 | rbd_create(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__lock__', 0) 29 | length = len(db) 30 | rbd_write(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__meta__', pack("!I%ss" % length, length, db), 0, length+4) 31 | except Exception as e: 32 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.create: Failed to create MetaDB: uri: %s" 33 | % (dbg, uri)) 34 | log.error(traceback.format_exc()) 35 | raise Exception(e) 36 | finally: 37 | cluster.shutdown() 38 | 39 | def destroy(self, dbg, uri): 40 | log.debug("%s: xcpng.librbd.meta.MetaDBOpeations.destroy: uri: %s" % (dbg, uri)) 41 | 42 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 43 | 44 | try: 45 | cluster.connect() 46 | rbd_remove(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__meta__') 47 | rbd_remove(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__lock__') 48 | 49 | except Exception as e: 50 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.destroy: Failed to destroy MetaDB: uri: %s" 51 | % (dbg, uri)) 52 | log.error(traceback.format_exc()) 53 | raise Exception(e) 54 | finally: 55 | cluster.shutdown() 56 | 57 | def load(self, dbg, uri): 58 | log.debug("%s: xcpng.libsbd.meta.MetaDBOpeations.load: uri: %s" % (dbg, uri)) 59 | 60 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 61 | 62 | try: 63 | cluster.connect() 64 | length = unpack('!I', rbd_read(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__meta__', 0, 4))[0] 65 | data = unpack('!%ss' % length, rbd_read(dbg, cluster, get_sr_name_by_uri(dbg, uri), '__meta__', 4, length))[0] 66 | return data 67 | except Exception as e: 68 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.load: Failed to load MetaDB: uri: %s" 69 | % (dbg, uri)) 70 | log.error(traceback.format_exc()) 71 | raise Exception(e) 72 | finally: 73 | cluster.shutdown() 74 | 75 | def dump(self, dbg, uri, json): 76 | log.debug("%s: xcpng.libsbd.meta.MetaDBOpeations.dump: uri: %s" % (dbg, uri)) 77 | 78 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 79 | 80 | try: 81 | length = len(json) 82 | cluster.connect() 83 | rbd_write(dbg, 84 | cluster, 85 | get_sr_name_by_uri(dbg, uri), 86 | '__meta__', 87 | pack("!I%ss" % length, length, json), 88 | 0, 89 | length + 4) 90 | except Exception as e: 91 | log.error("%s: xcpng.librbd.meta.MetaDBOpeations.dump: Failed to dump MetaDB: uri: %s" 92 | % (dbg, uri)) 93 | log.error(traceback.format_exc()) 94 | raise Exception(e) 95 | finally: 96 | cluster.shutdown() -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/volume.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import traceback 4 | 5 | from xapi.storage import log 6 | from xapi.storage.libs.xcpng.meta import IMAGE_UUID_TAG 7 | from xapi.storage.libs.xcpng.utils import get_cluster_name_by_uri, get_sr_name_by_uri, get_vdi_name_by_uri, \ 8 | roundup, VDI_PREFIXES, get_vdi_type_by_uri 9 | from xapi.storage.libs.xcpng.volume import VolumeOperations as _VolumeOperations_ 10 | from xapi.storage.libs.xcpng.librbd.rbd_utils import VOLBLOCKSIZE, ceph_cluster, rbd_create, rbd_remove, rbd_resize, \ 11 | rbd_utilization 12 | 13 | 14 | class VolumeOperations(_VolumeOperations_): 15 | 16 | def create(self, dbg, uri, size): 17 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.create: uri: %s size: %s" % (dbg, uri, size)) 18 | 19 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 20 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 21 | 22 | try: 23 | cluster.connect() 24 | rbd_create(dbg, 25 | cluster, 26 | get_sr_name_by_uri(dbg, uri), 27 | "%s%s" % (VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)], volume_meta[IMAGE_UUID_TAG]), size) 28 | except Exception as e: 29 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.create: Failed to create volume: uri: %s" 30 | % dbg, uri) 31 | log.error(traceback.format_exc()) 32 | raise Exception(e) 33 | finally: 34 | cluster.shutdown() 35 | 36 | def destroy(self, dbg, uri): 37 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.destroy: uri: %s" % (dbg, uri)) 38 | 39 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 40 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 41 | 42 | try: 43 | cluster.connect() 44 | rbd_remove(dbg, 45 | cluster, 46 | get_sr_name_by_uri(dbg, uri), 47 | "%s%s" % (VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)], volume_meta[IMAGE_UUID_TAG])) 48 | except Exception as e: 49 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.create: Failed to create volume: uri: %s" 50 | % dbg, uri) 51 | log.error(traceback.format_exc()) 52 | raise Exception(e) 53 | finally: 54 | cluster.shutdown() 55 | 56 | def resize(self, dbg, uri, new_size): 57 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.resize: uri: %s new_size: %s" % (dbg, uri, new_size)) 58 | 59 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 60 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 61 | 62 | try: 63 | cluster.connect() 64 | rbd_resize(dbg, 65 | cluster, 66 | get_sr_name_by_uri(dbg, uri), 67 | "%s%s" % (VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)], volume_meta[IMAGE_UUID_TAG]), 68 | new_size) 69 | except Exception as e: 70 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.resize: Failed to create volume: uri: %s new_size: %s" 71 | % dbg, uri, new_size) 72 | log.error(traceback.format_exc()) 73 | raise Exception(e) 74 | finally: 75 | cluster.shutdown() 76 | 77 | def get_phisical_utilization(self, dbg, uri): 78 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.get_phisical_utilization: uri: %s" % (dbg, uri)) 79 | 80 | volume_meta = self.MetadataHandler.get_vdi_meta(dbg, uri) 81 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 82 | 83 | try: 84 | cluster.connect() 85 | return rbd_utilization(dbg, 86 | cluster, 87 | get_sr_name_by_uri(dbg, uri), 88 | "%s%s" % (VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)], volume_meta[IMAGE_UUID_TAG])) 89 | except Exception as e: 90 | log.debug("%s: xcpng.librbd.volume.VolumeOperations.resize: Failed to create volume: uri: %s new_size: %s" 91 | % dbg, uri. new_size) 92 | log.error(traceback.format_exc()) 93 | raise Exception(e) 94 | finally: 95 | cluster.shutdown() 96 | 97 | def roundup_size(self, dbg, size): 98 | return roundup(VOLBLOCKSIZE, size) -------------------------------------------------------------------------------- /rpm/RBDSR.spec: -------------------------------------------------------------------------------- 1 | Summary: RBDSR - XenServer/XCP-ng Storage Manager plugin for CEPH 2 | Name: RBDSR 3 | Epoch: 3 4 | Version: 3.0 5 | Release: 1 6 | License: LGPL 7 | Group: Utilities/System 8 | BuildArch: noarch 9 | URL: https://github.com/rposudnevskiy/%{name} 10 | Requires: python-rbd 11 | Requires: rbd-nbd 12 | Requires: qemu 13 | Requires: qemu-dp 14 | Requires: glibc >= 2.17-222.el7 15 | %undefine _disable_source_fetch 16 | Source0: https://github.com/rposudnevskiy/%{name}/archive/v%{version}.zip 17 | 18 | %description 19 | This package contains RBDSR - XenServer/XCP-ng Storage Manager plugin for CEPH 20 | 21 | 22 | %prep 23 | %autosetup 24 | 25 | 26 | %install 27 | rm -rf %{builddir} 28 | rm -rf %{buildroot} 29 | #--- 30 | install -D -m 755 -o 0 -g 0 volume/org.xen.xapi.storage.rbdsr/plugin.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/plugin.py 31 | install -D -m 755 -o 0 -g 0 volume/org.xen.xapi.storage.rbdsr/sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/sr.py 32 | install -D -m 755 -o 0 -g 0 volume/org.xen.xapi.storage.rbdsr/volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/volume.py 33 | #--- 34 | install -D -m 755 -o 0 -g 0 datapath/rbd+raw+qdisk/plugin.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/plugin.py 35 | install -D -m 755 -o 0 -g 0 datapath/rbd+raw+qdisk/datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/datapath.py 36 | install -D -m 755 -o 0 -g 0 datapath/rbd+raw+qdisk/data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/data.py 37 | #--- 38 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/__init__.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/__init__.py 39 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/ceph_utils.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/ceph_utils.py 40 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/datapath.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/datapath.py 41 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/meta.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/meta.py 42 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/qemudisk.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/qemudisk.py 43 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/rbd_utils.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/rbd_utils.py 44 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/utils.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/utils.py 45 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/volume.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/volume.py 46 | wget -O xapi/storage/libs/librbd/qmp.py https://github.com/qemu/qemu/raw/stable-2.10/scripts/qmp/qmp.py 47 | install -D -m 755 -o 0 -g 0 xapi/storage/libs/librbd/qmp.py %{buildroot}/lib/python2.7/site-packages/xapi/storage/libs/librbd/qmp.py 48 | #--- 49 | ln -s plugin.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Plugin.diagnostics 50 | ln -s plugin.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Plugin.Query 51 | #--- 52 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.probe 53 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.attach 54 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.create 55 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.destroy 56 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.detach 57 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.ls 58 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.stat 59 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.set_description 60 | ln -s sr.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.set_name 61 | #--- 62 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.clone 63 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.create 64 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.destroy 65 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.resize 66 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set 67 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set_description 68 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set_name 69 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.snapshot 70 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.stat 71 | ln -s volume.py %{buildroot}/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.unset 72 | #--- 73 | ln -s plugin.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Plugin.Query 74 | #--- 75 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.activate 76 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.attach 77 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.close 78 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.deactivate 79 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.detach 80 | ln -s datapath.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Datapath.open 81 | #--- 82 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.copy 83 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.mirror 84 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.stat 85 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.cancel 86 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.destory 87 | ln -s data.py %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk/Data.ls 88 | #--- 89 | ln -s rbd+raw+qdisk %{buildroot}/usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk 90 | 91 | 92 | %files 93 | /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr 94 | /usr/libexec/xapi-storage-script/datapath/rbd+raw+qdisk 95 | /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk 96 | /lib/python2.7/site-packages/xapi/storage/libs/librbd 97 | 98 | 99 | %changelog 100 | * Sun Oct 07 2018 rposudnevskiy - 3.0-1 101 | - Added requirements for glibc >= 2.17-222.el7 (Issue #88) 102 | 103 | * Sun Oct 07 2018 rposudnevskiy - 3.0-1 104 | - Added Data interface 105 | 106 | * Tue Sep 25 2018 rposudnevskiy - 3.0-1 107 | - First packaging 108 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/sr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import traceback 4 | 5 | from xapi.storage import log 6 | from xapi.storage.libs.xcpng.sr import SROperations as _SROperations_ 7 | from xapi.storage.libs.xcpng.utils import POOL_PREFIX, SR_PATH_PREFIX, VDI_PREFIXES, \ 8 | get_sr_type_by_uri, get_sr_uuid_by_uri, mkdir_p, get_vdi_type_by_uri, \ 9 | get_sr_name_by_uri, get_cluster_name_by_uri, get_sr_uuid_by_name 10 | from xapi.storage.libs.xcpng.librbd.meta import CEPH_CLUSTER_TAG 11 | from xapi.storage.libs.xcpng.librbd.rbd_utils import get_config_files_list, pool_list, rbd_list, ceph_cluster 12 | 13 | 14 | class SROperations(_SROperations_): 15 | 16 | def __init__(self): 17 | self.DEFAULT_SR_NAME = '' 18 | self.DEFAULT_SR_DESCRIPTION = '' 19 | super(SROperations, self).__init__() 20 | 21 | def extend_uri(self, dbg, uri, configuration): 22 | log.debug("%s: xcpng.librbd.sr.SROperations.extend_uri: uri: %s configuration %s" % (dbg, uri, configuration)) 23 | 24 | if CEPH_CLUSTER_TAG in configuration: 25 | return "%s%s" % (uri, configuration[CEPH_CLUSTER_TAG]) 26 | else: 27 | return uri 28 | 29 | def create(self, dbg, uri, configuration): 30 | log.debug("%s: xcpng.librbd.sr.SROperations.create: uri: %s configuration %s" % (dbg, uri, configuration)) 31 | 32 | if CEPH_CLUSTER_TAG not in configuration: 33 | raise Exception('Failed to connect to CEPH cluster. Parameter \'cluster\' is not specified') 34 | 35 | cluster = ceph_cluster(dbg, configuration[CEPH_CLUSTER_TAG]) 36 | 37 | try: 38 | cluster.connect() 39 | cluster.create_pool(get_sr_name_by_uri(dbg, uri)) 40 | except Exception as e: 41 | log.debug("%s: xcpng.librbd.sr.SROperations.create: uri: Failed to create SR: uri: %s" 42 | % dbg, uri) 43 | log.error(traceback.format_exc()) 44 | raise Exception(e) 45 | finally: 46 | cluster.shutdown() 47 | 48 | def destroy(self, dbg, uri): 49 | log.debug("%s: xcpng.librbd.sr.SROperations.destroy: uri: %s" % (dbg, uri)) 50 | 51 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 52 | 53 | try: 54 | cluster.connect() 55 | cluster.delete_pool(get_sr_name_by_uri(dbg, uri)) 56 | except Exception as e: 57 | log.debug("%s: xcpng.librbd.sr.SROperations.destory: Failed to destroy SR: uri: %s" 58 | % dbg, uri) 59 | log.error(traceback.format_exc()) 60 | raise Exception(e) 61 | finally: 62 | cluster.shutdown() 63 | 64 | def get_sr_list(self, dbg, uri, configuration): 65 | log.debug("%s: xcpng.librbd.sr.SROperations.get_sr_list: uri: %s configuration %s" % (dbg, uri, configuration)) 66 | 67 | srs = [] 68 | uris = [] 69 | 70 | cluster_in_uri = get_cluster_name_by_uri(dbg, uri) 71 | 72 | log.debug("%s: xcpng.librbd.sr.SROperations.get_sr_list: uris: %s" % (dbg, uris)) 73 | 74 | if cluster_in_uri == '': 75 | for cluster in get_config_files_list(dbg): 76 | uris.append("%s/%s" % (uri[:-1], cluster)) 77 | else: 78 | uris = [uri] 79 | 80 | log.debug("%s: xcpng.librbd.sr.SROperations.get_sr_list: uris: %s" % (dbg, uris)) 81 | 82 | for _uri_ in uris: 83 | cluster_name = get_cluster_name_by_uri(dbg, _uri_) 84 | cluster = ceph_cluster(dbg, cluster_name) 85 | try: 86 | cluster.connect() 87 | for pool in pool_list(dbg, cluster): 88 | if pool.startswith("%s%s" % (get_sr_type_by_uri(dbg, uri), POOL_PREFIX)): 89 | srs.append("%s/%s" % (_uri_, get_sr_uuid_by_name(dbg, pool))) 90 | except Exception as e: 91 | log.debug("%s: xcpng.librbd.sr.SROperations.get_sr_list: uri: Failed to get SRs list: uri: %s" 92 | % dbg, uri) 93 | log.error(traceback.format_exc()) 94 | raise Exception(e) 95 | finally: 96 | cluster.shutdown() 97 | log.debug("%s: xcpng.librbd.sr.SROperations.get_sr_list: srs: %s" % (dbg, srs)) 98 | return srs 99 | 100 | def get_vdi_list(self, dbg, uri): 101 | log.debug("%s: xcpng.librbd.sr.SROperations.get_vdi_list: uri: %s" % (dbg, uri)) 102 | 103 | rbds = [] 104 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 105 | 106 | try: 107 | cluster.connect() 108 | for rbd in rbd_list(dbg, cluster, get_sr_name_by_uri(dbg, uri)): 109 | if rbd.startswith(VDI_PREFIXES[get_vdi_type_by_uri(dbg, uri)]): 110 | rbds.append(rbd) 111 | return rbds 112 | except Exception as e: 113 | log.debug("%s: xcpng.librbd.sr.SROperations.get_vdi_list: uri: Failed to get VDIs list: uri: %s" 114 | % dbg, uri) 115 | log.error(traceback.format_exc()) 116 | raise Exception(e) 117 | finally: 118 | cluster.shutdown() 119 | 120 | def sr_import(self, dbg, uri, configuration): 121 | log.debug("%s: xcpng.librbd.sr.SROperations.sr_import: uri: %s configuration %s" % (dbg, uri, configuration)) 122 | 123 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 124 | pool_name = get_sr_name_by_uri(dbg, uri) 125 | 126 | try: 127 | cluster.connect() 128 | if not cluster.pool_exists(pool_name): 129 | raise Exception("CEPH pool %s doesn\'t exist" % pool_name) 130 | except Exception as e: 131 | log.debug("%s: xcpng.librbd.sr.SROperations.sr_import: uri: Failed to import SR: uri: %s" 132 | % dbg, uri) 133 | log.error(traceback.format_exc()) 134 | raise Exception(e) 135 | finally: 136 | cluster.shutdown() 137 | 138 | mkdir_p("%s/%s" % (SR_PATH_PREFIX, get_sr_uuid_by_uri(dbg, uri))) 139 | 140 | def sr_export(self, dbg, uri): 141 | # log.debug("%s: xcpng.librbd.sr.SROperations.sr_export: uri: %s" % (dbg, uri)) 142 | pass 143 | 144 | def get_free_space(self, dbg, uri): 145 | log.debug("%s: xcpng.librbd.sr.SROperations.get_free_space: uri: %s" % (dbg, uri)) 146 | 147 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 148 | 149 | try: 150 | cluster.connect() 151 | stats = cluster.get_cluster_stats() 152 | return stats['kb_avail'] * 1024 153 | except Exception as e: 154 | log.debug("%s: xcpng.librbd.sr.SROperations.get_free_space: uri: Failed to get free space: uri: %s" 155 | % dbg, uri) 156 | log.error(traceback.format_exc()) 157 | raise Exception(e) 158 | finally: 159 | cluster.shutdown() 160 | 161 | def get_size(self, dbg, uri): 162 | log.debug("%s: xcpng.librbd.sr.SROperations.sr_size: uri: %s" % (dbg, uri)) 163 | 164 | cluster = ceph_cluster(dbg, get_cluster_name_by_uri(dbg, uri)) 165 | 166 | try: 167 | cluster.connect() 168 | stats = cluster.get_cluster_stats() 169 | return stats['kb'] * 1024 170 | except Exception as e: 171 | log.debug("%s: xcpng.librbd.sr.SROperations.get_size: uri: Failed to get size: uri: %s" 172 | % dbg, uri) 173 | log.error(traceback.format_exc()) 174 | raise Exception(e) 175 | finally: 176 | cluster.shutdown() 177 | -------------------------------------------------------------------------------- /src/xapi/storage/libs/xcpng/librbd/rbd_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from rbd import RBD, Image, ImageBusy, ImageExists 4 | from rados import Rados 5 | import os, fnmatch 6 | import traceback 7 | 8 | from xapi.storage import log 9 | 10 | VOLBLOCKSIZE = 2097152 11 | RBD_IMAGE_ORDER = 21 # the image is split into (2**order = VOLBLOCKSIZE) bytes objects 12 | 13 | def get_config_files_list(dbg): 14 | log.debug("%s: xcpng.librbd.rbd_utils.get_config_files_list" % (dbg)) 15 | files = [] 16 | pattern = '*.conf' 17 | if os.path.exists('/etc/ceph'): 18 | for file in os.listdir('/etc/ceph'): 19 | if fnmatch.fnmatch(file, pattern): 20 | files.append(os.path.splitext(os.path.basename(file))[0]) 21 | return files 22 | 23 | 24 | def ceph_cluster(dbg, cluster_name): 25 | log.debug("%s: xcpng.librbd.rbd_utils.ceph_cluster: Cluster: %s" % (dbg, cluster_name)) 26 | 27 | conf_file = "/etc/ceph/%s.conf" % cluster_name 28 | 29 | if not os.path.exists(conf_file): 30 | log.error("%s: xcpng.librbd.rbd_utils.ceph_cluster: Config file for CEPH cluster %s doesn't exists." % 31 | (dbg, cluster_name)) 32 | raise Exception("CEPH config file %s doesn't exists" % conf_file) 33 | 34 | cluster = Rados(conffile=conf_file) 35 | 36 | return cluster 37 | 38 | 39 | def rbd_list(dbg, cluster, pool): 40 | log.debug("%s: xcpng.librbd.rbd_utils.get_rbd_list: %s" % (dbg, pool)) 41 | ioctx = cluster.open_ioctx(pool) 42 | rbd_inst = RBD() 43 | try: 44 | rbds = rbd_inst.list(ioctx) 45 | return rbds 46 | except Exception as e: 47 | log.error("%s: xcpng.librbd.rbd_utils.get_rbd_list: Failed to get list of rbds for pool: %s " % (dbg, pool)) 48 | log.error(traceback.format_exc()) 49 | raise Exception(e) 50 | finally: 51 | ioctx.close() 52 | 53 | 54 | def pool_list(dbg, cluster): 55 | log.debug("%s: xcpng.librbd.rbd_utils.pool_list: Cluster ID: %s" % (dbg, cluster.get_fsid())) 56 | try: 57 | pools = cluster.list_pools() 58 | return pools 59 | except Exception as e: 60 | log.error("%s: xcpng.librbd.rbd_utils.get_pool_list: Failed to get list of pools for Cluster ID: %s" % 61 | (dbg, cluster.get_fsid())) 62 | log.error(traceback.format_exc()) 63 | raise Exception(e) 64 | 65 | 66 | def rbd_create(dbg, cluster, pool, name, size): 67 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_create: Cluster ID: %s Pool: %s Name: %s Size: %s" 68 | % (dbg, cluster.get_fsid(), pool, name, size)) 69 | ioctx = cluster.open_ioctx(pool) 70 | rbd_inst = RBD() 71 | try: 72 | rbd_inst.create(ioctx, name, size, order=RBD_IMAGE_ORDER) 73 | except Exception as e: 74 | log.error("%s: xcpng.librbd.rbd_utils.rbd_create: Failed to create an image: Cluster ID: %s Pool %s Name: %s Size: %s" 75 | % (dbg, cluster.get_fsid(), pool, name, size)) 76 | log.error(traceback.format_exc()) 77 | raise Exception(e) 78 | finally: 79 | ioctx.close() 80 | 81 | 82 | def rbd_remove(dbg, cluster, pool, name): 83 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_remove: Cluster ID: %s Pool %s Name: %s" 84 | % (dbg, cluster.get_fsid(), pool, name)) 85 | ioctx = cluster.open_ioctx(pool) 86 | rbd_inst = RBD() 87 | try: 88 | rbd_inst.remove(ioctx, name) 89 | except Exception as e: 90 | log.error("%s: xcpng.librbd.rbd_utils.rbd_remove: Failed to remove an image: Cluster ID: %s Pool %s Name: %s" 91 | % (dbg, cluster.get_fsid(), pool, name)) 92 | log.error(traceback.format_exc()) 93 | raise Exception(e) 94 | finally: 95 | ioctx.close() 96 | 97 | 98 | def rbd_resize(dbg, cluster, pool, name, size): 99 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_resize: Cluster ID: %s Pool: %s Name: %s Size: %s" 100 | % (dbg, cluster.get_fsid(), pool, name, size)) 101 | ioctx = cluster.open_ioctx(pool) 102 | image = Image(ioctx, name) 103 | 104 | try: 105 | image.resize(size) 106 | except Exception as e: 107 | log.error("%s: xcpng.librbd.rbd_utils.rbd_resize: Failed to resize an image: Cluster ID: %s Pool %s Name: %s Size: %s" 108 | % (dbg, cluster.get_fsid(), pool, name, size)) 109 | log.error(traceback.format_exc()) 110 | raise Exception(e) 111 | finally: 112 | ioctx.close() 113 | 114 | 115 | def rbd_utilization(dbg, cluster, pool, name): 116 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_utilization: Cluster ID: %s Pool: %s Name: %s" 117 | % (dbg, cluster.get_fsid(), pool, name)) 118 | ioctx = cluster.open_ioctx(pool) 119 | image = Image(ioctx, name) 120 | 121 | try: 122 | image_stat = image.stat() 123 | return image_stat['num_objs']*image_stat['obj_size'] 124 | except Exception as e: 125 | log.error("%s: xcpng.librbd.rbd_utils.rbd_utilisation: Failed to get an image utilization: Cluster ID: %s Pool %s Name: %s" 126 | % (dbg, cluster.get_fsid(), pool, name)) 127 | log.error(traceback.format_exc()) 128 | raise Exception(e) 129 | finally: 130 | ioctx.close() 131 | 132 | 133 | def rbd_rename(dbg, cluster, pool, old_name, new_name): 134 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_rename: Cluster ID: %s Pool: %s Old name: %s New name: %s" 135 | % (dbg, cluster.get_fsid(), pool, old_name, new_name)) 136 | 137 | ioctx = cluster.open_ioctx(pool) 138 | rbd_inst = RBD() 139 | 140 | try: 141 | rbd_inst.rename(ioctx, old_name, new_name) 142 | except Exception as e: 143 | log.error("%s: xcpng.librbd.rbd_utils.rbd_utilisation: Failed to get an image utilization: Cluster ID: %s Pool %s Old Name: %s New Name: %s" 144 | % (dbg, cluster.get_fsid(), pool, old_name, new_name)) 145 | log.error(traceback.format_exc()) 146 | raise Exception(e) 147 | finally: 148 | ioctx.close() 149 | 150 | 151 | def rbd_clone(dbg, cluster, parent_pool, parent, snapshot, clone_pool, clone): 152 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" 153 | % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) 154 | p_ioctx = cluster.open_ioctx(parent_pool) 155 | p_image = Image(p_ioctx, parent) 156 | c_ioctx = cluster.open_ioctx(clone_pool) 157 | rbd_inst = RBD() 158 | 159 | try: 160 | if not p_image.is_protected_snap(snapshot): 161 | p_image.protect_snap(snapshot) 162 | rbd_inst.clone(p_ioctx, parent, snapshot, c_ioctx, clone) 163 | except Exception as e: 164 | log.error("%s: xcpng.librbd.rbd_utils.rbd_clone: Failed to make a clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" 165 | % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) 166 | log.error(traceback.format_exc()) 167 | raise Exception(e) 168 | finally: 169 | p_ioctx.close() 170 | c_ioctx.close() 171 | 172 | 173 | def rbd_snapshot(dbg, cluster, pool, name, snapshot): 174 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_snapshot: Cluster ID: %s Pool: %s Name: %s Snapshot: %s" 175 | % (dbg, cluster.get_fsid(), pool, name, snapshot)) 176 | ioctx = cluster.open_ioctx(pool) 177 | image = Image(ioctx, name) 178 | 179 | try: 180 | image.create_snap(snapshot) 181 | except Exception as e: 182 | log.error("%s: xcpng.librbd.rbd_utils.rbd_clone: Failed to take a snapshot: Cluster ID: %s Pool: %s Name: %s Snapshot: %s" 183 | % (dbg, cluster.get_fsid(), pool, name, snapshot)) 184 | log.error(traceback.format_exc()) 185 | raise Exception(e) 186 | finally: 187 | ioctx.close() 188 | 189 | 190 | def rbd_exists(dbg, cluster, pool, name): 191 | log.debug("%s: rbd_utils.rbd_exist: Cluster ID: %s Image: %s" 192 | % (dbg, cluster.get_fsid(), name)) 193 | ioctx = cluster.open_ioctx(pool) 194 | try: 195 | Image(ioctx, name) 196 | return True 197 | except Exception: 198 | return False 199 | finally: 200 | ioctx.close() 201 | 202 | 203 | def rbd_lock(dbg, cluster, pool, name): 204 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_lock: Cluster ID: %s Pool: %s Name: %s" 205 | % (dbg, cluster.get_fsid(), pool, name)) 206 | ioctx = cluster.open_ioctx(pool) 207 | image = Image(ioctx, name) 208 | try: 209 | image.lock_exclusive('xapi-xcpng-lock') 210 | return ioctx, image 211 | except ImageBusy or ImageExists as e: 212 | log.error("%s: xcpng.librbd.rbd_utils.rbd_lock: Failed to acquire exclusive lock: Cluster ID: %s Pool: %s Name: %s" 213 | % (dbg, cluster.get_fsid(), pool, name)) 214 | log.error(traceback.format_exc()) 215 | image.close() 216 | ioctx.close() 217 | raise Exception(e) 218 | 219 | def is_locked(dbg, cluster, pool, name): 220 | log.debug("%s: xcpng.librbd.rbd_utils.is_locked: Cluster ID: %s Pool: %s Name: %s" 221 | % (dbg, cluster.get_fsid(), pool, name)) 222 | ioctx = cluster.open_ioctx(pool) 223 | image = Image(ioctx, name) 224 | if len(image.list_lockers()) > 0: 225 | return True 226 | else: 227 | return False 228 | 229 | 230 | def rbd_unlock(dbg, lh): 231 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_unlock" % (dbg)) 232 | lh[2].unlock('xapi-xcpng-lock') 233 | lh[2].close() 234 | lh[1].close() 235 | 236 | 237 | def rbd_read(dbg, cluster, pool, name, offset, length): 238 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_read: Cluster ID: %s Pool: %s Name: %s Offset: %s Length: %s" 239 | % (dbg, cluster.get_fsid(), pool, name, offset, length)) 240 | ioctx = cluster.open_ioctx(pool) 241 | try: 242 | image = Image(ioctx, name) 243 | return image.read(offset, length) 244 | except ImageBusy or ImageExists as e: 245 | log.error("%s: xcpng.librbd.rbd_utils.rbd_read: Failed to read from the image: Cluster ID: %s Pool: %s Name: %s" 246 | % (dbg, cluster.get_fsid(), pool, name)) 247 | log.error(traceback.format_exc()) 248 | raise Exception(e) 249 | finally: 250 | ioctx.close() 251 | 252 | 253 | def rbd_write(dbg, cluster, pool, name, data, offset, length): 254 | log.debug("%s: xcpng.librbd.rbd_utils.rbd_wite: Cluster ID: %s Pool: %s Name: %s Offset: %s Length: %s" 255 | % (dbg, cluster.get_fsid(), pool, name, offset, length)) 256 | ioctx = cluster.open_ioctx(pool) 257 | try: 258 | image = Image(ioctx, name) 259 | image.write(data, offset) 260 | except ImageBusy or ImageExists as e: 261 | log.error("%s: xcpng.librbd.rbd_utils.rbd_lock: Failed to write to the image: Cluster ID: %s Pool: %s Name: %s" 262 | % (dbg, cluster.get_fsid(), pool, name)) 263 | log.error(traceback.format_exc()) 264 | raise Exception(e) 265 | finally: 266 | ioctx.close() 267 | -------------------------------------------------------------------------------- /install/RBDSR.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DEFAULT_CEPH_VERSION="nautilus" 3 | 4 | # Usage: installCephRepo 5 | function installCephRepo { 6 | echo "Install Ceph Repo" 7 | yum install --enablerepo="extras,base" -q -y centos-release-ceph-$1.noarch 8 | echo "centos" > /etc/yum/vars/contentdir 9 | } 10 | 11 | # Usage: removeCephRepo 12 | function removeCephRepo { 13 | yum erase -y centos-release-ceph-$1.noarch 14 | } 15 | 16 | # Usage: removeXCPngRepo 17 | #function removeXCPngRepo { 18 | # rm -f /etc/yum.repos.d/xcp-ng.repo 19 | #} 20 | 21 | # Usage: installXCPngRepo 22 | #function installXCPngRepo { 23 | # major_version=`cat /etc/centos-release | awk '{print $3}' | awk -F. '{print $1}'` 24 | # major_minor_version=`cat /etc/centos-release | awk '{print $3}' | awk -F. '{print $1"."$2}'` 25 | # cat << EOF >/etc/yum.repos.d/xcp-ng.repo 26 | #[xcp-ng-extras_testing] 27 | #name=XCP-ng Extras Testing Repository 28 | #baseurl=https://updates.xcp-ng.org/${major_version}/${major_minor_version}/extras_testing/x86_64/ 29 | #enabled=0 30 | #gpgcheck=0 31 | #EOF 32 | #} 33 | 34 | # Usage: confirmInstallation 35 | function confirmInstallation { 36 | echo "This script is going to install 'RBDSR' storage plugin and its dependencies" 37 | echo "which include 'xcp-ng-extras_testing' repository, upgrade 'glibc' and 'qemu-dp' packages." 38 | echo "Please note that Ceph support is experimental and can lead to an unstable system and data loss" 39 | default='no' 40 | while true; do 41 | read -p "Continue? (y[es]/n[o]) [${default}]: " yesorno 42 | if [ -z ${yesorno} ];then 43 | yesorno=${default} 44 | fi 45 | case ${yesorno} in 46 | yes|y) 47 | ret=0 48 | break 49 | ;; 50 | no|n) 51 | ret=1 52 | break 53 | ;; 54 | esac 55 | done 56 | return ${ret} 57 | } 58 | 59 | # Usage: setReposEnabled
<0|1> 60 | function setReposEnabled { 61 | echo "Set $1 $2 enabled = $3" 62 | sed -ie "/\[$2\]/,/^\[/s/enabled=[01]/enabled=$3/" /etc/yum.repos.d/$1 63 | } 64 | 65 | # Usage: backupFile 66 | function backupFile { 67 | echo "Backing Up file $1" 68 | if [ -e $1.bkp ]; then 69 | echo "$1.bkp already in place, not backing up!" 70 | else 71 | mv $1 $1-orig 72 | fi 73 | } 74 | 75 | # Usage: restoreFile 76 | function restoreFile { 77 | echo "Restore file $1" 78 | if [ -e $1.bkp ]; then 79 | mv $1.bkp $1 80 | else 81 | echo "No $1-orig in place, not restoring!" 82 | fi 83 | } 84 | 85 | # Usage: copyFile 86 | function copyFile { 87 | cp $1 $2 88 | chmod +x $2 89 | } 90 | 91 | function configureFirewall { 92 | iptables -A INPUT -p tcp --dport 6789 -j ACCEPT 93 | iptables -A INPUT -m multiport -p tcp --dports 6800:7300 -j ACCEPT 94 | service iptables save 95 | } 96 | 97 | function unconfigureFirewall { 98 | iptables -D INPUT -p tcp --dport 6789 -j ACCEPT 99 | iptables -D INPUT -m multiport -p tcp --dports 6800:7300 -j ACCEPT 100 | service iptables save 101 | } 102 | 103 | function installCeph { 104 | echo "Install Ceph API" 105 | yum install --enablerepo="extras,base" -q -y python-rbd rbd-nbd 106 | } 107 | 108 | function uninstallCeph { 109 | echo "Uninstall Ceph API" 110 | yum erase -q -y python-rbd rbd-nbd 111 | } 112 | 113 | function upgradeDeps { 114 | yum install --enablerepo="extras,base" -q -y glibc-2.17-222.el7 115 | } 116 | 117 | function downgradeDeps { 118 | yum history undo -q -y `yum history packages-list glibc | head -4 | tail -1 | awk -F\| '{gsub(/ /, "", $0); print $1}'` 119 | } 120 | 121 | function installFiles { 122 | echo "Install RBDSR Files" 123 | rm -rf /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr 124 | mkdir -p /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr 125 | 126 | copyFile "src/volume/org.xen.xapi.storage.rbdsr/plugin.json" "/usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/plugin.json" 127 | 128 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/plugin.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/plugin.py 129 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/sr.py 130 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/volume.py 131 | 132 | ln -s plugin.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Plugin.diagnostics 133 | ln -s plugin.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Plugin.Query 134 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.probe 135 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.attach 136 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.create 137 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.destroy 138 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.detach 139 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.ls 140 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.stat 141 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.set_description 142 | ln -s sr.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/SR.set_name 143 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.clone 144 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.create 145 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.destroy 146 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.resize 147 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set 148 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set_description 149 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.set_name 150 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.snapshot 151 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.stat 152 | ln -s volume.py /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr/Volume.unset 153 | 154 | rm -rf /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk 155 | mkdir -p /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk 156 | 157 | copyFile "src/datapath/rbd+qcow2+qdisk/plugin.json" "/usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/plugin.json" 158 | 159 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/plugin.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/plugin.py 160 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/datapath.py 161 | ln -s /lib/python2.7/site-packages/xapi/storage/libs/xcpng/scripts/data.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/data.py 162 | 163 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.activate 164 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.attach 165 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.close 166 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.deactivate 167 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.detach 168 | ln -s datapath.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Datapath.open 169 | ln -s plugin.py /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk/Plugin.Query 170 | 171 | rm -rf /lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd 172 | mkdir /lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd 173 | 174 | copyFile "src/xapi/storage/libs/xcpng/librbd/__init__.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/__init__.py" 175 | copyFile "src/xapi/storage/libs/xcpng/librbd/datapath.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/datapath.py" 176 | copyFile "src/xapi/storage/libs/xcpng/librbd/locks.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/locks.py" 177 | copyFile "src/xapi/storage/libs/xcpng/librbd/meta.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/meta.py" 178 | copyFile "src/xapi/storage/libs/xcpng/librbd/rbd_utils.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/rbd_utils.py" 179 | copyFile "src/xapi/storage/libs/xcpng/librbd/sr.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/sr.py" 180 | copyFile "src/xapi/storage/libs/xcpng/librbd/volume.py" "/lib/python2.7/site-packages/xapi/storage/libs/xcpng/librbd/volume.py" 181 | } 182 | 183 | function removeFiles { 184 | echo "Removing RBDSR Files" 185 | rm -rf /usr/libexec/xapi-storage-script/volume/org.xen.xapi.storage.rbdsr 186 | rm -rf /usr/libexec/xapi-storage-script/datapath/rbd+qcow2+qdisk 187 | rm -rf /lib/python2.7/site-packages/xapi/storage/libs/librbd 188 | } 189 | 190 | function install { 191 | installCephRepo $1 192 | installCeph 193 | installFiles 194 | upgradeDeps 195 | configureFirewall 196 | } 197 | 198 | function deinstall { 199 | unconfigureFirewall 200 | downgradeDeps 201 | removeFiles 202 | uninstallCeph 203 | removeCephRepo $1 204 | } 205 | 206 | case $1 in 207 | install) 208 | if confirmInstallation; then 209 | if [ -z "$2" ]; then 210 | install ${DEFAULT_CEPH_VERSION} 211 | else 212 | if [ -z `echo $2|egrep "^jewel$|^kraken$|^luminous$|^mimic$"` ]; then 213 | echo "[ERROR]: Unsupported Ceph version specified '$2'" 214 | exit 1 215 | else 216 | install $2 217 | fi 218 | fi 219 | fi 220 | ;; 221 | deinstall) 222 | CEPH_INSTALLED_VERSION=`ls /etc/yum.repos.d/ | awk 'match($0, /CentOS-Ceph-(.*).repo/, a) {print a[1]}' | awk '{print tolower($0)}'` 223 | if [ -z "${CEPH_INSTALLED_VERSION}" ]; then 224 | echo "[ERROR]: Can't determine installed version of Ceph." 225 | echo " RBDSR plugin is not installed or corrupted." 226 | exit 2 227 | fi 228 | if [ -z "$2" ]; then 229 | deinstall ${CEPH_INSTALLED_VERSION} 230 | else 231 | if [ "$2" != "${CEPH_INSTALLED_VERSION}" ]; then 232 | echo "[ERROR]: Installed version of Ceph is '${CEPH_INSTALLED_VERSION}'" 233 | exit 3 234 | else 235 | deinstall $2 236 | fi 237 | fi 238 | ;; 239 | *) 240 | echo "Usage: $0 install|deinstall [jewel|kraken|luminous|mimic]" 241 | exit 1 242 | ;; 243 | esac 244 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | --------------------------------------------------------------------------------