def resize_from_voldrv(volumename, volumesize, volumepath, storagedriver_id): """ Resize a disk Triggered by volumedriver messages on the queue @param volumepath: path on hypervisor to the volume @param volumename: volume id of the disk @param volumesize: size of the volume """ pmachine = PMachineList.get_by_storagedriver_id(storagedriver_id) storagedriver = StorageDriverList.get_by_storagedriver_id( storagedriver_id) hypervisor = Factory.get(pmachine) volumepath = hypervisor.clean_backing_disk_filename(volumepath) mutex = VolatileMutex('{}_{}'.format(volumename, volumepath)) try: mutex.acquire(wait=30) disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is None: disk = VDiskList.get_by_devicename_and_vpool( volumepath, storagedriver.vpool) if disk is None: disk = VDisk() finally: mutex.release() disk.devicename = volumepath disk.volume_id = volumename disk.size = volumesize disk.vpool = storagedriver.vpool disk.save()
def devicename_exists(self, vpool, name=None, names=None): """ Checks whether a given name can be created on the vpool :param vpool: vPool object :type vpool: VPool :param name: Candidate name :type name: str :param names: Candidate names :type names: list :return: Whether the devicename exists :rtype: bool """ error_message = None if not (name is None) ^ (names is None): error_message = 'Either the name (string) or the names (list of strings) parameter must be passed' if name is not None and not isinstance(name, basestring): error_message = 'The name parameter must be a string' if names is not None and not isinstance(names, list): error_message = 'The names parameter must be a list of strings' if error_message is not None: raise HttpNotAcceptableException(error_description=error_message, error='impossible_request') if name is not None: devicename = VDiskController.clean_devicename(name) return VDiskList.get_by_devicename_and_vpool(devicename, vpool) is not None for name in names: devicename = VDiskController.clean_devicename(name) if VDiskList.get_by_devicename_and_vpool(devicename, vpool) is not None: return True return False
def resize_from_voldrv(volumename, volumesize, volumepath, storagedriver_id): """ Resize a disk Triggered by volumedriver messages on the queue @param volumepath: path on hypervisor to the volume @param volumename: volume id of the disk @param volumesize: size of the volume """ pmachine = PMachineList.get_by_storagedriver_id(storagedriver_id) storagedriver = StorageDriverList.get_by_storagedriver_id(storagedriver_id) hypervisor = Factory.get(pmachine) volumepath = hypervisor.clean_backing_disk_filename(volumepath) mutex = VolatileMutex('{}_{}'.format(volumename, volumepath)) try: mutex.acquire(wait=30) disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is None: disk = VDiskList.get_by_devicename_and_vpool(volumepath, storagedriver.vpool) if disk is None: disk = VDisk() finally: mutex.release() disk.devicename = volumepath disk.volume_id = volumename disk.size = volumesize disk.vpool = storagedriver.vpool disk.save() VDiskController.sync_with_mgmtcenter(disk, pmachine, storagedriver) MDSServiceController.ensure_safety(disk)
def getVDisk(path, vpool=None, timeout=None): url = urlparse.urlparse(path) if url.netloc == "": path = path.replace("{}:".format(url.scheme), "{}://".format(url.scheme)) url = urlparse.urlparse(path) path = "/" + url.path.strip("/") path = path.split(":")[0] # cause yeah putting nonestandard url params if not path.endswith(".raw"): path += ".raw" elif not url.scheme: parts = url.path.split("/")[1:] # remove first slash if parts[0] == "mnt": vpool = parts[1] path = "/" + "/".join(parts[2:]) vpool = _getVPoolByUrl(url, vpool) disk = VDiskList.get_by_devicename_and_vpool(path, vpool) if timeout is not None: start = time.time() while not disk and start + timeout > time.time(): time.sleep(2) disk = VDiskList.get_by_devicename_and_vpool(path, vpool) return disk
def test_sync_vdisk_with_voldrv(self): clone_depth = 3 def _make_clones(vdisks_map, depth=clone_depth): for level in range(depth): previous_vd = list(vdisks_map.itervalues())[-1] new_name = previous_vd.name + '_clone' new_guid = VDiskController.clone(previous_vd.guid, new_name).get('vdisk_guid') vdisks_map[new_name] = VDisk(new_guid) structure = DalHelper.build_dal_structure({ 'vpools': [1], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1)] } # (<id>, <storagedriver_id>) ) vdisk_name = 'vdisk_1' storagedriver = structure['storagedrivers'][1] vdisk_1 = VDisk( VDiskController.create_new(volume_name=vdisk_name, volume_size=1024**4, storagedriver_guid=storagedriver.guid)) vdisks = OrderedDict() vdisks[vdisk_name] = vdisk_1 _make_clones(vdisks) self.assertEquals(clone_depth + 1, len(list(VDiskList.get_vdisks()))) delete_list = list(vdisks.itervalues( ))[::-1][:-1] # These vDisks are clones and ought to be deleted for vdisk in delete_list: for mds_service in vdisk.mds_services: mds_service.delete() vdisk.delete() self.assertEquals(1, len(list(VDiskList.get_vdisks())) ) # Make sure vDisk clones are properly removed self.assertEquals( VDiskList.get_vdisks()[0].name, vdisk_name) # Make sure only item left is original vDisk VDiskController.sync_with_reality() self.assertEquals(clone_depth + 1, len(list( VDiskList.get_vdisks()))) # The clones should be in place now parents = 0 for vdisk in VDiskList.get_vdisks(): try: if vdisk.parent_vdisk.name: parents += 1 except AttributeError: pass self.assertEquals( clone_depth, parents ) # As much parents should be detected as the depth of the clones
def test_event_resize_from_volumedriver(self): """ Test resize from volumedriver event - Create a vDisk using the resize event - Resize the created vDisk using the same resize event """ structure = Helper.build_service_structure( {'vpools': [1], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1)]} # (<id>, <storagedriver_id>) ) vpools = structure['vpools'] storagedrivers = structure['storagedrivers'] mds_service = structure['mds_services'][1] # Create volume using resize from voldrv device_name = '/vdisk.raw' srclient = StorageRouterClient(vpools[1].guid, None) mds_backend_config = Helper._generate_mdsmetadatabackendconfig([mds_service]) volume_id = srclient.create_volume(device_name, mds_backend_config, 1024 ** 4, str(storagedrivers[1].storagedriver_id)) VDiskController.resize_from_voldrv(volume_id=volume_id, volume_size=1024 ** 4, volume_path=device_name, storagedriver_id=storagedrivers[1].storagedriver_id) vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 1, msg='Expected to find 1 vDisk in model') self.assertEqual(first=vdisks[0].name, second='vdisk', msg='Volume name should be vdisk') self.assertEqual(first=vdisks[0].volume_id, second=volume_id, msg='Volume ID should be {0}'.format(volume_id)) self.assertEqual(first=vdisks[0].devicename, second=device_name, msg='Device name should be {0}'.format(device_name)) self.assertEqual(first=vdisks[0].size, second=1024 ** 4, msg='Size should be 1 TiB') # Resize volume using resize from voldrv VDiskController.resize_from_voldrv(volume_id=volume_id, volume_size=2 * 1024 ** 4, volume_path=device_name, storagedriver_id=storagedrivers[1].storagedriver_id) vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 1, msg='Expected to find 1 vDisk in model') self.assertEqual(first=vdisks[0].name, second='vdisk', msg='Volume name should be vdisk') self.assertEqual(first=vdisks[0].size, second=2 * 1024 ** 4, msg='Size should be 2 TiB')
def test_clone_from_template_happypath(self): """ Test clone from template - happy path """ StorageDriverModule.use_good_client() vdisk_1_1, pmachine = self._prepare() VDiskController.create_from_template(vdisk_1_1.guid, 'vmachine_2', 'vdisk_1_1-clone', pmachine.guid) vdisks = VDiskList.get_vdisk_by_name('vdisk_1_1') self.assertEqual(len(vdisks), 1, 'Vdisk not modeled') clones = VDiskList.get_vdisk_by_name('vdisk_1_1-clone') self.assertEqual(len(clones), 1, 'Clone not modeled')
def update_vdisk_name(volume_id, old_name, new_name): """ Update a vDisk name using Management Center: set new name """ vdisk = None for mgmt_center in MgmtCenterList.get_mgmtcenters(): mgmt = Factory.get_mgmtcenter(mgmt_center = mgmt_center) try: disk_info = mgmt.get_vdisk_device_info(volume_id) device_path = disk_info['device_path'] vpool_name = disk_info['vpool_name'] vp = VPoolList.get_vpool_by_name(vpool_name) file_name = os.path.basename(device_path) vdisk = VDiskList.get_by_devicename_and_vpool(file_name, vp) if vdisk: break except Exception as ex: logger.info('Trying to get mgmt center failed for disk {0} with volume_id {1}. {2}'.format(old_name, volume_id, ex)) if not vdisk: logger.error('No vdisk found for name {0}'.format(old_name)) return vpool = vdisk.vpool mutex = VolatileMutex('{}_{}'.format(old_name, vpool.guid if vpool is not None else 'none')) try: mutex.acquire(wait=5) vdisk.name = new_name vdisk.save() finally: mutex.release()
def get_vdisk_by_name(vdisk_name, vpool_name): """ Fetch disk partitions by disk guid :param vdisk_name: location of a vdisk on a vpool (e.g. /mnt/vpool/test.raw = test.raw, /mnt/vpool/volumes/test.raw = volumes/test.raw ) :type vdisk_name: str :param vpool_name: name of a existing vpool :type vpool_name: str :return: a vdisk object :rtype: ovs.dal.hybrids.vdisk """ vpool = VPoolList.get_vpool_by_name(vpool_name) if vpool: if not vdisk_name.startswith("/"): vdisk_name = "/{0}".format(vdisk_name) if not vdisk_name.endswith('.raw'): vdisk_name = '{0}.raw'.format(vdisk_name) vdisk = VDiskList.get_by_devicename_and_vpool(vdisk_name, vpool) if vdisk: return vdisk else: raise VDiskNotFoundError( "VDisk with name `{0}` not found on vPool `{1}`!".format( vdisk_name, vpool_name)) else: raise VPoolNotFoundError( "vPool with name `{0}` cannot be found!".format(vpool_name))
def _find_ovs_model_disk_by_location(self, location, hostname, retry=3, timeout=3): """Find OVS disk object based on location and hostname :return VDisk: OVS DAL model object """ hostname = self._get_real_hostname(hostname) LOG.debug('[_FIND OVS DISK] Location %s, hostname %s' % (location, hostname)) attempt = 0 while attempt <= retry: for vd in VDiskList.get_vdisks(): if vd.vpool: for vsr in vd.vpool.storagedrivers: if vsr.storagerouter.name == hostname: _location = "{0}/{1}".format(vsr.mountpoint, vd.devicename) if _location == location: LOG.info('Location %s Disk found %s' % (location, vd.guid)) disk = VDisk(vd.guid) return disk msg = ' NO RESULT Attempt %s timeout %s max attempts %s' LOG.debug(msg % (attempt, timeout, retry)) if timeout: time.sleep(timeout) attempt += 1 raise RuntimeError('No disk found for location %s' % location)
def delete_volume(vdisk, vpool, loop_device=None, root_client=None, wait=True): """ Delete a volume :param vdisk: Virtual disk to delete :param vpool: vPool which hosts the Virtual Disk :param loop_device: Loop device where volume is mounted on :param root_client: SSHClient object :param wait: Wait for the volume to be deleted from model :return: None """ location = GeneralVDisk.get_filesystem_location(vpool=vpool, vdisk_name=vdisk.name) if root_client is None: root_client = SSHClient('127.0.0.1', username='******') if loop_device is not None: GeneralVDisk.disconnect_volume(loop_device, root_client) root_client.file_delete(location) if wait is True: counter = 0 timeout = 60 volume_name = '/' + os.path.basename(location) while True and counter < timeout: time.sleep(1) vdisks = VDiskList.get_by_devicename_and_vpool(volume_name, vpool) if vdisks is None: break counter += 1 if counter == timeout: raise RuntimeError('Disk {0} was not deleted from model after {1} seconds'.format(volume_name, timeout))
def list(self, vpoolguid=None, storagerouterguid=None, query=None): """ Overview of all vDisks :param vpoolguid: Guid of the vPool to retrieve its disks :type vpoolguid: str :param storagerouterguid: Guid of the StorageRouter to retrieve its disks :type storagerouterguid: str :param query: A query to be executed if required :type query: DataQuery """ if vpoolguid is not None: vpool = VPool(vpoolguid) vdisks = vpool.vdisks elif storagerouterguid is not None: storagerouter = StorageRouter(storagerouterguid) vdisks = DataList( VDisk, { 'type': DataList.where_operator.AND, 'items': [('guid', DataList.operator.IN, storagerouter.vdisks_guids)] }) else: vdisks = VDiskList.get_vdisks() if query is not None: query_vdisk_guids = DataList(VDisk, query).guids vdisks = [ vdisk for vdisk in vdisks if vdisk.guid in query_vdisk_guids ] return vdisks
def update_vdisk_name(volume_id, old_name, new_name): """ Update a vDisk name using Management Center: set new name """ vdisk = None for mgmt_center in MgmtCenterList.get_mgmtcenters(): mgmt = Factory.get_mgmtcenter(mgmt_center=mgmt_center) try: disk_info = mgmt.get_vdisk_device_info(volume_id) device_path = disk_info['device_path'] vpool_name = disk_info['vpool_name'] vp = VPoolList.get_vpool_by_name(vpool_name) file_name = os.path.basename(device_path) vdisk = VDiskList.get_by_devicename_and_vpool(file_name, vp) if vdisk: break except Exception as ex: logger.info( 'Trying to get mgmt center failed for disk {0} with volume_id {1}. {2}' .format(old_name, volume_id, ex)) if not vdisk: logger.error('No vdisk found for name {0}'.format(old_name)) return vpool = vdisk.vpool mutex = VolatileMutex('{}_{}'.format( old_name, vpool.guid if vpool is not None else 'none')) try: mutex.acquire(wait=5) vdisk.name = new_name vdisk.save() finally: mutex.release()
def get_vdisk_by_name(name): """ Retrieve the DAL vDisk object based on its name :param name: Name of the virtual disk :return: vDisk DAL object """ return VDiskList.get_vdisk_by_name(vdiskname=name)
def create_new(diskname, size, storagedriver_guid): """ Create a new vdisk/volume using filesystem calls :param diskname: name of the disk :param size: size of the disk (GB) :param storagedriver_guid: guid of the Storagedriver :return: guid of the new disk """ logger.info('Creating new empty disk {0} of {1} GB'.format(diskname, size)) storagedriver = StorageDriver(storagedriver_guid) vp_mountpoint = storagedriver.mountpoint hypervisor = Factory.get(storagedriver.storagerouter.pmachine) disk_path = hypervisor.clean_backing_disk_filename(hypervisor.get_disk_path(None, diskname)) location = os.path.join(vp_mountpoint, disk_path) VDiskController.create_volume(location, size) backoff = 1 timeout = 30 # seconds start = time.time() while time.time() < start + timeout: vdisk = VDiskList.get_by_devicename_and_vpool(disk_path, storagedriver.vpool) if vdisk is None: logger.debug('Waiting for disk to be picked up by voldrv') time.sleep(backoff) backoff += 1 else: return vdisk.guid raise RuntimeError('Disk {0} was not created in {1} seconds.'.format(diskname, timeout))
def delete_from_voldrv(volumename, storagedriver_id): """ Delete a disk Triggered by volumedriver messages on the queue @param volumename: volume id of the disk """ _ = storagedriver_id # For logging purposes disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: mutex = VolatileMutex('{}_{}'.format(volumename, disk.devicename)) try: mutex.acquire(wait=20) pmachine = None try: pmachine = PMachineList.get_by_storagedriver_id(disk.storagedriver_id) except RuntimeError as ex: if 'could not be found' not in str(ex): raise # else: pmachine can't be loaded, because the volumedriver doesn't know about it anymore if pmachine is not None: limit = 5 hypervisor = Factory.get(pmachine) exists = hypervisor.file_exists(disk.vpool, disk.devicename) while limit > 0 and exists is True: time.sleep(1) exists = hypervisor.file_exists(disk.vpool, disk.devicename) limit -= 1 if exists is True: logger.info('Disk {0} still exists, ignoring delete'.format(disk.devicename)) return logger.info('Delete disk {}'.format(disk.name)) disk.delete() finally: mutex.release()
def snapshot_all_vdisks(): """ Snapshots all vDisks """ GenericController._logger.info('[SSA] started') success = [] fail = [] for vdisk in VDiskList.get_vdisks(): if vdisk.is_vtemplate is True: continue try: metadata = { 'label': '', 'is_consistent': False, 'timestamp': str(int(time.time())), 'is_automatic': True, 'is_sticky': False } VDiskController.create_snapshot(vdisk_guid=vdisk.guid, metadata=metadata) success.append(vdisk.guid) except Exception: GenericController._logger.exception( 'Error taking snapshot for vDisk {0}'.format(vdisk.guid)) fail.append(vdisk.guid) GenericController._logger.info( '[SSA] Snapshot has been taken for {0} vDisks, {1} failed.'.format( len(success), len(fail))) return success, fail
def list(self, vpoolguid=None, storagerouterguid=None, query=None): """ Overview of all vDisks :param vpoolguid: Guid of the vPool to retrieve its disks :type vpoolguid: str :param storagerouterguid: Guid of the StorageRouter to retrieve its disks :type storagerouterguid: str :param query: A query to be executed if required :type query: DataQuery """ if vpoolguid is not None: vpool = VPool(vpoolguid) vdisks = vpool.vdisks elif storagerouterguid is not None: storagerouter = StorageRouter(storagerouterguid) vdisks = DataList( VDisk, { "type": DataList.where_operator.AND, "items": [("guid", DataList.operator.IN, storagerouter.vdisks_guids)], }, ) else: vdisks = VDiskList.get_vdisks() if query is not None: query_vdisk_guids = DataList(VDisk, query).guids vdisks = [vdisk for vdisk in vdisks if vdisk.guid in query_vdisk_guids] return vdisks
def list(self, vpoolguid=None, storagerouterguid=None): """ Overview of all vDisks :param vpoolguid: Guid of the vPool to retrieve its disks :type vpoolguid: str :param storagerouterguid: Guid of the StorageRouter to retrieve its disks :type storagerouterguid: str :return: List of vDisks matching the parameters specified :rtype: list[ovs.dal.hybrids.vdisk.VDisk] """ if vpoolguid is not None: vpool = VPool(vpoolguid) vdisks = vpool.vdisks elif storagerouterguid is not None: storagerouter = StorageRouter(storagerouterguid) vdisks = DataList( VDisk, { 'type': DataList.where_operator.AND, 'items': [('guid', DataList.operator.IN, storagerouter.vdisks_guids)] }) else: vdisks = VDiskList.get_vdisks() return vdisks
def _find_ovs_model_disk_by_location(self, location, hostname, retry=3, timeout=3): """Find OVS disk object based on location and hostname :return VDisk: OVS DAL model object """ hostname = self._get_real_hostname(hostname) LOG.debug('[_FIND OVS DISK] Location %s, hostname %s' % (location, hostname)) attempt = 0 while attempt <= retry: for vd in VDiskList.get_vdisks(): if vd.vpool: for vsr in vd.vpool.storagedrivers: if vsr.storagerouter.name == hostname: _location = "{0}/{1}".format( vsr.mountpoint, vd.devicename) if _location == location: LOG.info('Location %s Disk found %s' % (location, vd.guid)) disk = VDisk(vd.guid) return disk msg = ' NO RESULT Attempt %s timeout %s max attempts %s' LOG.debug(msg % (attempt, timeout, retry)) if timeout: time.sleep(timeout) attempt += 1 raise RuntimeError('No disk found for location %s' % location)
def volumedriver_error(code, volumename): """ Handles error messages/events from the volumedriver """ if code == VolumeDriverEvents.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: MDSServiceController.ensure_safety(disk)
def volumedriver_error(code, volumename, storagedriver_id): """ Handles error messages/events from the volumedriver """ _ = storagedriver_id # Required for the @log decorator if code == VolumeDriverEvents.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: MDSServiceController.ensure_safety(disk)
def test_delete(self): """ Test the delete of a vDisk - Create 2 vDisks with identical names on 2 different vPools - Delete 1st vDisk and verify other still remains on correct vPool - Delete 2nd vDisk and verify no more volumes left """ structure = DalHelper.build_dal_structure({ 'vpools': [1, 2], 'domains': [1], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1), (2, 2, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1), (2, 2)] } # (<id>, <storagedriver_id>) ) domains = structure['domains'] storagedrivers = structure['storagedrivers'] vdisk1 = VDisk( VDiskController.create_new( volume_name='vdisk_1', volume_size=1024**3, storagedriver_guid=storagedrivers[1].guid)) vdisk2 = VDisk( VDiskController.create_new( volume_name='vdisk_1', volume_size=1024**3, storagedriver_guid=storagedrivers[2].guid)) vdisk_domain = VDiskDomain() vdisk_domain.domain = domains[1] vdisk_domain.vdisk = vdisk1 vdisk_domain.save() # Delete vDisk1 and make some assertions VDiskController.delete(vdisk_guid=vdisk1.guid) with self.assertRaises(ObjectNotFoundException): VDisk(vdisk1.guid) self.assertEqual( first=len(VDiskController.list_volumes()), second=1, msg='Expected to find only 1 volume in Storage Driver list_volumes' ) self.assertIn(member=vdisk2, container=VDiskList.get_vdisks(), msg='vDisk2 should still be modeled') # Delete vDisk2 and make some assertions VDiskController.delete(vdisk_guid=vdisk2.guid) with self.assertRaises(ObjectNotFoundException): VDisk(vdisk2.guid) self.assertEqual( first=len(VDiskController.list_volumes()), second=0, msg= 'Expected to find no more volumes in Storage Driver list_volumes')
def _snapshot_has_children(self, snapshotid): """Find if snapshot has children, in OVS Model :return True/False """ LOG.debug('[_FIND CHILDREN OF SNAPSHOT] Snapshotid %s' % snapshotid) for vdisk in VDiskList.get_vdisks(): if vdisk.parentsnapshot == snapshotid: return True return False
def volumedriver_error(code, volumename): """ Handles error messages/events from the volumedriver :param code: Volumedriver error code :param volumename: Name of the volume throwing the error """ if code == VolumeDriverEvents.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: MDSServiceController.ensure_safety(disk)
def _vdisks_guids(self): """ Gets the vDisk guids served by this StorageDriver. """ from ovs.dal.lists.vdisklist import VDiskList volume_ids = [] for entry in self.vpool.objectregistry_client.get_all_registrations(): if entry.node_id() == self.storagedriver_id: volume_ids.append(entry.object_id()) return VDiskList.get_in_volume_ids(volume_ids).guids
def _vdisks_guids(self): """ Gets the vDisk guids served by this StorageRouter. """ from ovs.dal.lists.vdisklist import VDiskList vdisk_guids = [] for storagedriver in self.storagedrivers: storagedriver_client = storagedriver.vpool.storagedriver_client vdisk_guids += VDiskList.get_in_volume_ids(storagedriver_client.list_volumes(str(storagedriver.storagedriver_id))).guids return vdisk_guids
def test_clone_from_template_error_handling(self): """ Test clone from template - error during create """ StorageDriverModule.use_bad_client() vdisk_1_1, pmachine = self._prepare() self.assertRaises(RuntimeError, VDiskController.create_from_template, vdisk_1_1.guid, 'vmachine_2', 'vdisk_1_1-clone', pmachine.guid) clones = VDiskList.get_vdisk_by_name('vdisk_1_1-clone') self.assertIsNone(clones, 'Clone not deleted after exception')
def list(self, vmachineguid=None, vpoolguid=None): """ Overview of all vDisks """ if vmachineguid is not None: vmachine = VMachine(vmachineguid) return vmachine.vdisks elif vpoolguid is not None: vpool = VPool(vpoolguid) return vpool.vdisks return VDiskList.get_vdisks()
def _find_ovs_model_disk_by_snapshot_id(self, snapshotid): """Find OVS disk object based on snapshot id :return VDisk: OVS DAL model object """ LOG.debug("[_FIND OVS DISK] Snapshotid %s" % snapshotid) for disk in VDiskList.get_vdisks(): snaps_guid = [s["guid"] for s in disk.snapshots] if str(snapshotid) in snaps_guid: LOG.info("[_FIND OVS DISK] Snapshot id %s Disk found %s" % (snapshotid, disk.name)) return disk raise RuntimeError("No disk found for snapshotid %s" % snapshotid)
def _ovs_snapshot_id_in_vdisklist_snapshots(self, snapshot_id, retry=10): attempt = 0 while attempt <= int(retry): snap_map = dict((vd.guid, vd.snapshots) for vd in VDiskList.get_vdisks()) for guid, snapshots in snap_map.items(): snaps = [snap['guid'] for snap in snapshots if snap['guid'] == snapshot_id] if len(snaps) == 1: return True attempt += 1 time.sleep(2) return False
def test_list_volumes(self): """ Test the list volumes functionality - Create 1 vDisk on vPool1 and create 3 vDisks on vPool2 - List all volumes - List the volumes on vPool1 - List the volumes on vPool2 """ structure = Helper.build_service_structure( {'vpools': [1, 2], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1), (2, 2, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1), (2, 2)]} # (<id>, <storagedriver_id>) ) vpools = structure['vpools'] storagedrivers = structure['storagedrivers'] vpool1 = vpools[1] vpool2 = vpools[2] VDiskController.create_new(volume_name='vdisk_1', volume_size=1024 ** 4, storagedriver_guid=storagedrivers[1].guid) VDiskController.create_new(volume_name='vdisk_1', volume_size=1024 ** 4, storagedriver_guid=storagedrivers[2].guid) VDiskController.create_new(volume_name='vdisk_2', volume_size=1024 ** 4, storagedriver_guid=storagedrivers[2].guid) VDiskController.create_new(volume_name='vdisk_3', volume_size=1024 ** 4, storagedriver_guid=storagedrivers[2].guid) all_vdisks = VDiskList.get_vdisks() # List all volumes sd_volume_ids = set(VDiskController.list_volumes()) model_volume_ids = set([vdisk.volume_id for vdisk in all_vdisks]) self.assertEqual(first=len(sd_volume_ids), second=4, msg='Expected to retrieve all 4 volumes') self.assertEqual(first=sd_volume_ids, second=model_volume_ids, msg='Volume IDs from Storage Driver not identical to volume IDs in model. SD: {0} - Model: {1}'.format(sd_volume_ids, model_volume_ids)) # List all volumes of vpools[1] sd_vpool1_volume_ids = set(VDiskController.list_volumes(vpool_guid=vpool1.guid)) model_vpool1_volume_ids = set([vdisk.volume_id for vdisk in all_vdisks if vdisk.vpool == vpool1]) self.assertEqual(first=len(sd_vpool1_volume_ids), second=1, msg='Expected to retrieve 1 volume') self.assertEqual(first=sd_vpool1_volume_ids, second=model_vpool1_volume_ids, msg='Volume IDs for vPool1 from Storage Driver not identical to volume IDs in model. SD: {0} - Model: {1}'.format(sd_vpool1_volume_ids, model_vpool1_volume_ids)) # List all volumes of vpools[2] sd_vpool2_volume_ids = set(VDiskController.list_volumes(vpool_guid=vpool2.guid)) model_vpool2_volume_ids = set([vdisk.volume_id for vdisk in all_vdisks if vdisk.vpool == vpool2]) self.assertEqual(first=len(sd_vpool2_volume_ids), second=3, msg='Expected to retrieve 3 volumes') self.assertEqual(first=sd_vpool2_volume_ids, second=model_vpool2_volume_ids, msg='Volume IDs for vPool2 from Storage Driver not identical to volume IDs in model. SD: {0} - Model: {1}'.format(sd_vpool2_volume_ids, model_vpool2_volume_ids))
def volumedriver_error(code, volumename, storagedriver_id): """ Handles error messages/events from the volumedriver :param code: Volumedriver error code :param volumename: Name of the volume throwing the error :param storagedriver_id: ID of the storagedriver hosting the volume """ _ = storagedriver_id # Required for the @log decorator if code == VolumeDriverEvents.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: MDSServiceController.ensure_safety(disk)
def _find_ovs_model_disk_by_snapshot_id(self, snapshotid): """Find OVS disk object based on snapshot id :return VDisk: OVS DAL model object """ LOG.debug('[_FIND OVS DISK] Snapshotid %s' % snapshotid) for disk in VDiskList.get_vdisks(): snaps_guid = [s['guid'] for s in disk.snapshots] if str(snapshotid) in snaps_guid: LOG.info('[_FIND OVS DISK] Snapshot id %s Disk found %s' % (snapshotid, disk)) return disk raise RuntimeError('No disk found for snapshotid %s' % snapshotid)
def get_vdisks_stats(): """ Send vdisks statistics to InfluxDB """ vdisks = VDiskList.get_vdisks() if len(vdisks) == 0: StatsmonkeyScheduledTaskController._logger.info("No vdisks found") return None for vdisk in vdisks: try: points = [] metrics = StatsmonkeyScheduledTaskController._pop_realtime_info(vdisk.statistics) disk_name = vdisk.name failover_mode = vdisk.info['failover_mode'] if failover_mode in ['OK_STANDALONE', 'OK_SYNC']: failover_status = 0 elif failover_mode == 'CATCHUP': failover_status = 1 elif failover_mode == 'DEGRADED': failover_status = 2 else: failover_status = 3 metrics['failover_mode_status'] = failover_status if vdisk.vmachine: vm_name = vdisk.vmachine.name else: vm_name = None vpool_name = VPool(vdisk.vpool_guid).name entry = { 'measurement': 'vdisk_stats', 'tags': { 'disk_name': disk_name, 'vm_name': vm_name, 'storagerouter_name': StorageRouter(vdisk.storagerouter_guid).name, 'vpool_name': vpool_name, 'failover_mode': vdisk.info['failover_mode'] }, 'fields': metrics } points.append(entry) StatsmonkeyScheduledTaskController._send_stats(points) return points except Exception as ex: StatsmonkeyScheduledTaskController._logger.error(ex.message) return None return None
def _vmachines_guids(self): """ Gets the vMachine guids served by this StorageRouter. Definition of "served by": vMachine whose disks are served by a given StorageRouter """ from ovs.dal.lists.vdisklist import VDiskList vmachine_guids = set() for storagedriver in self.storagedrivers: storagedriver_client = storagedriver.vpool.storagedriver_client for vdisk in VDiskList.get_in_volume_ids(storagedriver_client.list_volumes(str(storagedriver.storagedriver_id))): if vdisk.vmachine_guid is not None: vmachine_guids.add(vdisk.vmachine_guid) return list(vmachine_guids)
def list(self, vmachineguid=None, vpoolguid=None): """ Overview of all vDisks :param vmachineguid: Guid of the virtual machine to retrieve its disks :param vpoolguid: Guid of the vPool to retrieve its disks """ if vmachineguid is not None: vmachine = VMachine(vmachineguid) return vmachine.vdisks elif vpoolguid is not None: vpool = VPool(vpoolguid) return vpool.vdisks return VDiskList.get_vdisks()
def volumedriver_error(code, volume_id): """ Handles error messages/events from the volumedriver :param code: Volumedriver error code :type code: int :param volume_id: Name of the volume throwing the error :type volume_id: str :return: None """ if code == VolumeDriverEvents.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volume_id) if disk is not None: MDSServiceController.ensure_safety(disk)
def volumedriver_error(code, volume_id): """ Handles error messages/events from the volumedriver :param code: Volumedriver error code :type code: int :param volume_id: Name of the volume throwing the error :type volume_id: str :return: None """ if code == VolumeDriverEvents_pb2.MDSFailover: disk = VDiskList.get_vdisk_by_volume_id(volume_id) if disk is not None: MDSServiceController.ensure_safety(disk)
def getVDisk(path, vpool=None, timeout=None): url = urlparse.urlparse(path) if url.netloc == '': path = path.replace('{}:'.format(url.scheme), '{}://'.format(url.scheme)) url = urlparse.urlparse(path) path = '/' + url.path.strip('/') path = path.split(':')[0] # cause yeah putting nonestandard url params if not path.endswith('.raw'): path += '.raw' elif not url.scheme: parts = url.path.split('/')[1:] # remove first slash if parts[0] == 'mnt': vpool = parts[1] path = '/' + '/'.join(parts[2:]) vpool = _getVPoolByUrl(url, vpool) disk = VDiskList.get_by_devicename_and_vpool(path, vpool) if timeout is not None: start = time.time() while not disk and start + timeout > time.time(): time.sleep(2) disk = VDiskList.get_by_devicename_and_vpool(path, vpool) return disk
def _vdisks_guids(self): """ Gets the vDisk guids served by this StorageRouter. """ from ovs.dal.lists.vdisklist import VDiskList volume_ids = [] vpools = set() storagedriver_ids = [] for storagedriver in self.storagedrivers: vpools.add(storagedriver.vpool) storagedriver_ids.append(storagedriver.storagedriver_id) for vpool in vpools: for entry in vpool.objectregistry_client.get_all_registrations(): if entry.node_id() in storagedriver_ids: volume_ids.append(entry.object_id()) return VDiskList.get_in_volume_ids(volume_ids).guids
def create_volume(size, vpool, name=None, loop_device=None, root_client=None, wait=True): """ Create a volume :param size: Size of the volume (in GB) :param vpool: vPool to create a volume for :param name: Name for the volume :param loop_device: Loop device to use to mount volume on :param root_client: SSHClient object :param wait: Wait for the volume to be created on volumedriver and in model :return: Newly created Virtual Disk """ location = GeneralVDisk.get_filesystem_location(vpool=vpool, vdisk_name=name if name is not None else uuid.uuid4()) if root_client is None: root_client = SSHClient('127.0.0.1', username='******') try: if loop_device is not None: root_client.run('umount /mnt/{0}'.format(loop_device), allow_nonzero=True, allow_insecure=True) root_client.run(['truncate', '-s', '{0}G'.format(size), location]) root_client.dir_create(['/mnt/{0}'.format(loop_device)]) root_client.run(['mkfs.ext4', '-F', location]) root_client.run(['mount', '-o', 'loop', location, '/mnt/{0}'.format(loop_device)]) else: root_client.run(['truncate', '-s', '{0}G'.format(size), location]) except CalledProcessError as cpe: GeneralVDisk.logger.error(str(cpe)) if loop_device is not None: root_client.run('umount /mnt/{0}'.format(loop_device), allow_nonzero=True, allow_insecure=True) root_client.run('rm {0}'.format(location), allow_nonzero=True, allow_insecure=True) root_client.run('rmdir /mnt/{0}'.format(loop_device), allow_nonzero=True, allow_insecure=True) raise vdisk = None if wait is True: counter = 0 timeout = 60 volume_name = '/' + os.path.basename(location) while True and counter < timeout: time.sleep(1) vdisk = VDiskList.get_by_devicename_and_vpool(volume_name, vpool) if vdisk is not None: break counter += 1 if counter == timeout: raise RuntimeError('Disk {0} did not show up in model after {1} seconds'.format(volume_name, timeout)) return vdisk
def mds_catchup(): """ Looks to catch up all MDS slaves which are too far behind Only one catch for every storagedriver is invoked """ # Only for caching purposes def storagedriver_worker(queue, error_list): # type: (Queue.Queue, List[str]) -> None while not queue.empty(): mds_catch_up = queue.get() # type: MDSCatchUp try: mds_catch_up.catch_up(async=False) except Exception as ex: MDSServiceController._logger.exception( 'Exceptions while catching for vDisk {0}'.format( mds_catch_up.vdisk.guid)) error_list.append(str(ex)) finally: queue.task_done() storagedriver_queues = {} for vdisk in VDiskList.get_vdisks(): if vdisk.storagedriver_id not in storagedriver_queues: storagedriver_queues[vdisk.storagedriver_id] = Queue.Queue() # Putting it in the Queue ensures that the reference is still there so the caching is used optimally catch_up = MDSCatchUp(vdisk.guid) storagedriver_queues[vdisk.storagedriver_id].put(catch_up) errors = [] threads = [] for storadriver_id, storagedriver_queue in storagedriver_queues.iteritems( ): thread = Thread(target=storagedriver_worker, args=( storagedriver_queue, errors, )) thread.start() threads.append(thread) for thread in threads: thread.join() if len(errors) > 0: raise RuntimeError( 'Exception occurred while catching up: \n - {0}'.format( '\n - '.join(errors)))
def test_clone_from_template_error_handling2(self): """ Test clone from template - error during create, then error during delete """ StorageDriverModule.use_bad_client() global VDisk def delete(self, *args, **kwargs): raise RuntimeError('DAL Error') _delete = VDisk.delete VDisk.delete = delete vdisk_1_1, pmachine = self._prepare() self.assertRaises(RuntimeError, VDiskController.create_from_template, vdisk_1_1.guid, 'vmachine_2', 'vdisk_1_1-clone', pmachine.guid) clones = VDiskList.get_vdisk_by_name('vdisk_1_1-clone') self.assertEqual(len(clones), 1, 'Clone deleted') VDisk.delete = _delete
def dtl_state_transition(volume_name, old_state, new_state, storagedriver_id): """ Triggered by volumedriver when DTL state changes :param volume_name: ID of the volume :param old_state: Previous DTL status :param new_state: New DTL status :param storagedriver_id: ID of the storagedriver hosting the volume :return: None """ if new_state == VolumeDriverEvents_pb2.Degraded and old_state != VolumeDriverEvents_pb2.Standalone: vdisk = VDiskList.get_vdisk_by_volume_id(volume_name) if vdisk: logger.info('Degraded DTL detected for volume {0} with guid {1}'.format(vdisk.name, vdisk.guid)) storagedriver = StorageDriverList.get_by_storagedriver_id(storagedriver_id) VDiskController.dtl_checkup(vdisk_guid=vdisk.guid, storagerouters_to_exclude=[storagedriver.storagerouter.guid], chain_timeout=600)
def _ovs_devicename_in_vdisklist(self, devicename, exists = True, retry=10): if devicename is None: raise RuntimeError('Devicename is None, expecting a string.') self._debug('find device {0} in ovs model'.format(devicename)) attempt = 0 while attempt <= int(retry): self._get_vpool() vdisk = VDiskList.get_by_devicename_and_vpool(devicename, self.vpool) if exists: if vdisk is not None: return True else: if vdisk is None: return True self._debug('not found, sleep 1') attempt += 1 time.sleep(2) self._debug('still not found, return') return False
def delete_from_voldrv(volumename, storagedriver_id): """ Delete a disk Triggered by volumedriver messages on the queue @param volumename: volume id of the disk """ _ = storagedriver_id # For logging purposes disk = VDiskList.get_vdisk_by_volume_id(volumename) if disk is not None: mutex = VolatileMutex('{}_{}'.format(volumename, disk.devicename)) try: mutex.acquire(wait=20) pmachine = None try: pmachine = PMachineList.get_by_storagedriver_id( disk.storagedriver_id) except RuntimeError as ex: if 'could not be found' not in str(ex): raise # else: pmachine can't be loaded, because the volumedriver doesn't know about it anymore if pmachine is not None: limit = 5 storagedriver = StorageDriverList.get_by_storagedriver_id( storagedriver_id) hypervisor = Factory.get(pmachine) exists = hypervisor.file_exists(storagedriver, disk.devicename) while limit > 0 and exists is True: time.sleep(1) exists = hypervisor.file_exists( storagedriver, disk.devicename) limit -= 1 if exists is True: logger.info( 'Disk {0} still exists, ignoring delete'.format( disk.devicename)) return logger.info('Delete disk {}'.format(disk.name)) for mds_service in disk.mds_services: mds_service.delete() disk.delete() finally: mutex.release()